Remove Old Docs (#657)

* Update the main page

* Remove old docs

* Remove old version links and add link to legacy docs

* Fix site URLs in mkdocs.yml

* Move 4.0 docs to top level

* Update fix search index script

* Fix font CSS links

* Update 404 and gitignore

* Update CI and remove all other 4.0 links

* Fix font issue still loading from google

* Update NL index page, remove link to vapor core team (inaccessible)

Co-authored-by: De Bock Benny <bdebock@protonmail.com>
This commit is contained in:
Tim Condon 2022-05-09 15:43:23 +01:00 committed by GitHub
parent 9567690bdf
commit 3d14e1b79a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
338 changed files with 117 additions and 24217 deletions

View File

@ -12,16 +12,12 @@ jobs:
uses: actions/checkout@v3
- name: Install dependencies
run: |
composer global require couscous/couscous
pip install -r requirements.txt
curl -OSL https://couscous.io/couscous.phar
sudo chmod +x couscous.phar
sudo mv couscous.phar /usr/local/bin/couscous
- name: Build the website
run: |
bash ./build.sh
mkdocs build
swift fixSearchIndex.swift
cp googlefc012e5d94cfa05f.html site/googlefc012e5d94cfa05f.html;
- name: Configure AWS credentials
id: cred
uses: aws-actions/configure-aws-credentials@v1

5
.gitignore vendored
View File

@ -1,9 +1,4 @@
/.couscous
.sass-cache
.DS_Store
leaf-pygment/dist
/site
/2.0/site
/3.0/site
/4.0/site

3
1.5/.gitignore vendored
View File

@ -1,3 +0,0 @@
/.couscous
.sass-cache
.DS_Store

View File

@ -1,7 +0,0 @@
# Contributing to Vapor Docs
Found a mistake or want to add something? Fork the documentation, fix it, and submit a pull request.
We'll merge it as soon as we can.
Thanks!

View File

@ -1,89 +0,0 @@
# Documentación de Vapor
[![Stack Overflow](https://img.shields.io/stackexchange/stackoverflow/t/vapor.svg)](http://stackoverflow.com/questions/tagged/vapor)
Esta es la documentación de Vapor, el _framework web_ para Swift que funciona sobre iOS, macOS y ubuntu; y sobre todos los _paquetes_ que Vapor ofrece.
Vapor es el _framework web_ más utilizado para Swift. Proporciona una base maravillosamente expresiva y fácil de usar para tu próximo sitio web o API.
Para ver el código fuente y la documentación del código visita [Vapor's GitHub](https://github.com/vapor/vapor).
Para leer esto en [正體中文](https://github.com/vapor/documentation/1.5/README.zh-hant.md)
Para leer esto en [简体中文](https://github.com/vapor/documentation/blob/README.zh-cn.md)
Para leer esto en [English](https://github.com/vapor/documentation/1.5/README.md)
## Cómo leer esta documentación.
Puedes leer esta guía haciendo clic en las carpetas y los archivos de [GitHub](https://github.com/vapor/documentation) o a través de las páginas generadas [GitHub Pages](https://vapor.github.io/documentation/).
## API
La documentación de la API generada automáticamente se encuentra en [api.vapor.codes](http://api.vapor.codes).
## Paquetes
Aquí hay una lista de todos los paquetes y módulos incluidos con Vapor (también _utilizables_ individualmente).
- [Vapor](https://github.com/vapor/vapor): Swift el _framework web_ más utilizado.
- Auth: Autenticación y persistencia de usuarios.
- Sessions: Almacenamiento de datos seguro y _efímero_ basado en cookies.
- Cookies: Cookies HTTP.
- Routing: Enrutador avanzado con parametrización segura.
- [Fluent](https://github.com/vapor/fluent): Modelos, relaciones y consulta de bases de datos NoSQL y SQL.
- [Engine](https://github.com/vapor/engine): Capas de transporte principales.
- HTTP: Cliente y servidor HTTP completamente en Swift.
- URI: Parseo y _serialización_ completamente en Swift.
- WebSockets: Canales de comunicación full-duplex a través de una sola conexión TCP.
- SMTP: Envío de correo electrónico con SendGrill y Gmail.
- [Leaf](https://github.com/vapor/leaf): Un lenguaje de plantillas extensible.
- [JSON](https://github.com/vapor/json): Mapas Jay JSON a tipos de Vapor.
- [Console](https://github.com/vapor/console): Wrapper en Swift para E/S de consola y comandos.
- [TLS](https://github.com/vapor/tls): Wrapper en Swift para el nuevo TLS de CLibreSSL.
- [Crypto](https://github.com/vapor/crypto): Criptografía de LibreSSL y Swift.
- Digests: _Hashing_ con y sin autenticación.
- Ciphers: Encriptación y descifrado.
- Random: Pseudo aleatoriedad criptográficamente segura.
- BCrypt: Implementación completamente en Swift.
- [Node](https://github.com/vapor/node): Estructura de datos para fáciles conversiones de tipo.
- [Polymorphic](https://github.com/vapor/polymorphic): Sintaxis para acceder fácilmente a valores de tipos comunes como JSON.
- [Path Indexable](https://github.com/vapor/path-indexable): Un protocolo para un acceso poderoso via _subscript_ a tipos comunes como JSON.
- [Core](https://github.com/vapor/core): Extensiones básicas, _alias_ de tipos, y funciones que facilitan tareas comunes.
- [Socks](https://github.com/vapor/socks): _API Wrapper_ para acceder a sockets en C.
## Proveedores y otros.
Aquí hay una lista de proveedores y paquetes de terceros que funcionan muy bien con Vapor.
- [MySQL](https://github.com/vapor/mysql): Interface robusta MySQL para Swift.
- [MySQL Driver](https://github.com/vapor/mysql-driver): _Driver_ MySQL para Fluent.
- [MySQL Provider](https://github.com/vapor/mysql-provider): Proveedor MySQL para Vapor.
- [SQLite](https://github.com/vapor/sqlite): _Wrapper_ SQLite 3 para Swift
- [SQLite Driver](https://github.com/vapor/sqlite-driver): _Driver_ SQLite para Fluent.
- [SQLite Provider](https://github.com/vapor/sqlite-provider): Proveedor SQLite provider para Vapor.
- [PostgreSQL](https://github.com/vapor/postgresql): Interface PostgreSQL robusta para Swift.
- [PostgreSQL Driver](https://github.com/vapor/postgresql-driver): _Driver_ PostgreSQL para Fluent.
- [PostgreSQL Provider](https://github.com/vapor/postgresql-provider): Proveedor PostgreSQL para Vapor.
- [MongoKitten*](https://github.com/OpenKitten/MongoKitten): _Driver_ nativo MongoDB, escrito en Swift
- [Mongo Driver](https://github.com/vapor/mongo-driver): _Driver_ MongoKitten para Fluent.
- [Mongo Provider](https://github.com/vapor/mongo-provider): Proveedor MongoKitten para Vapor.
- [MainecoonVapor](https://github.com/OpenKitten/MainecoonVapor): MongoKitten ORM para Vapor.
- [Redbird](https://github.com/vapor/redbird): Un cliente Redis completamente en Swift implementado directamente desde la especificación del protocolo.
- [Redis Provider](https://github.com/vapor/redis-provider): Proveedor del _cache_ de Redis para Vapor.
- [Kitura Provider](https://github.com/vapor/kitura-provider): Permite usar el servidor HTTP de IBM (Kitura) en Vapor.
- [SwiftyBeaver](https://github.com/SwiftyBeaver/SwiftyBeaver-Vapor): Agrega el potente _logging_ de SwiftyBeaver a Vapor.
- [APNS](https://github.com/matthijs2704/vapor-apns): Sencilla biblioteca APNS para Vapor (Swift).
- [VaporFCM](https://github.com/mdab121/vapor-fcm): Sencilla biblioteca FCM para Vapor.
- [JWT](https://github.com/siemensikkema/vapor-jwt): Implementación JWT para Vapor.
- [VaporS3Signer](https://github.com/JustinM1/VaporS3Signer): Gerera _V4 Auth Header/Pre-Signed URL_ para _AWS S3 REST API_.
- [Flock](https://github.com/jakeheis/Flock): _Despliegue_ automatizado de proyectos Swift en servidores.
- [VaporFlock](https://github.com/jakeheis/VaporFlock): Utiliza Flock para _desplegar_ aplicaciones de vapor
- [VaporForms](https://github.com/bygri/vapor-forms): Brinda a Vapor un manejo de formularios web simple, dinámico y _reutilizable_.
- [Jobs](https://github.com/BrettRToomey/Jobs): Un sistema minimalista para ejecutar _jobs_/tareas en _2o plano_ para Swift.
- [Heimdall](https://github.com/himani93/heimdall): Un _logger_ de _requet's_ HTTP fácil de usar.
## Autores
[Tanner Nelson](mailto:tanner@qutheory.io), [Logan Wright](mailto:logan@qutheory.io), y los cientos de miembros de Vapor.

View File

@ -1,92 +0,0 @@
# Vapor Documentation
[![Stack Overflow](https://img.shields.io/stackexchange/stackoverflow/t/vapor.svg)](http://stackoverflow.com/questions/tagged/vapor)
This is the documentation for Vapor, a Web Framework for Swift that works on iOS, macOS, and Ubuntu; and all of the packages that Vapor offers.
Vapor is the most used web framework for Swift. It provides a beautifully expressive and easy to use foundation for your next website or API.
To view the framework's source code and code documentation, visit [Vapor's GitHub](https://github.com/vapor/vapor).
Read this in [Spanish](https://github.com/vapor/documentation/1.5/README.es.md)
Read this in [正體中文](https://github.com/vapor/documentation/1.5/README.zh-hant.md)
Read this in [简体中文](https://github.com/vapor/documentation/1.5/README.zh-cn.md)
## How To Read
You can read this guide by clicking through the folders and markdown files on [GitHub](https://github.com/vapor/documentation) or through the rendered [GitHub Pages](https://vapor.github.io/documentation/).
## API
Auto-generated API documentation is located at [api.vapor.codes](http://api.vapor.codes).
## Packages
Here are a list of all the packages and modules included with Vapor (also useable individually).
- [Vapor](https://github.com/vapor/vapor): Swift's most used web framework.
- Auth: User authentication and persistance.
- Sessions: Secure, ephemeral cookie based data storage.
- Cookies: HTTP cookies.
- Routing: Advanced router with type-safe parameterization.
- [Fluent](https://github.com/vapor/fluent): Models, relationships, and querying for NoSQL and SQL databases.
- [Engine](https://github.com/vapor/engine): Core transport layers.
- HTTP: Pure Swift HTTP client and server.
- URI: Pure Swift URI parsing and serializing.
- WebSockets: Full-duplex communication channels over a single TCP connection.
- SMTP: Send email using Sendgrid and Gmail.
- [Multipart](https://github.com/vapor/multipart): Fast, streaming, non-blocking multipart parser and serializer.
- Multipart: Parses and serializes `multipart/mixed`.
- FormData: Parses and serializes `multipart/form-data`.
- [Leaf](https://github.com/vapor/leaf): An extensible templating language.
- [JSON](https://github.com/vapor/json): Maps Jay JSON to Vapor types.
- [Console](https://github.com/vapor/console): Swift wrapper for console IO and commands.
- [TLS](https://github.com/vapor/tls): Swift wrapper for CLibreSSL's new TLS.
- [Crypto](https://github.com/vapor/crypto): Cryptography from LibreSSL and Swift.
- Digests: Hashing with and without authentication.
- Ciphers: Encryption and decryption
- Random: Pseudo and cryptographically secure randomness.
- BCrypt: Pure Swift implementation.
- [Node](https://github.com/vapor/node): Data structure for easy type conversions.
- [Polymorphic](https://github.com/vapor/polymorphic): Syntax for easily accessing values from common types like JSON.
- [Path Indexable](https://github.com/vapor/path-indexable): A protocol for powerful subscript access of common types like JSON.
- [Core](https://github.com/vapor/core): Core extensions, type-aliases, and functions that facilitate common tasks.
- [Socks](https://github.com/vapor/socks): Swift C Socket API wrapper.
## Providers & Other
Here are a list of providers and third party packages that work great with Vapor.
- [MySQL](https://github.com/vapor/mysql): Robust MySQL interface for Swift.
- [MySQL Driver](https://github.com/vapor/mysql-driver): MySQL driver for Fluent.
- [MySQL Provider](https://github.com/vapor/mysql-provider): MySQL provider for Vapor.
- [SQLite](https://github.com/vapor/sqlite): SQLite 3 wrapper for Swift
- [SQLite Driver](https://github.com/vapor/sqlite-driver): SQLite driver for Fluent.
- [SQLite Provider](https://github.com/vapor/sqlite-provider): SQLite provider for Vapor.
- [PostgreSQL](https://github.com/vapor/postgresql): Robust PostgreSQL interface for Swift.
- [PostgreSQL Driver](https://github.com/vapor/postgresql-driver): PostgreSQL driver for Fluent.
- [PostgreSQL Provider](https://github.com/vapor/postgresql-provider): PostgreSQL provider for Vapor.
- [MongoKitten*](https://github.com/OpenKitten/MongoKitten): Native MongoDB driver for Swift, written in Swift
- [Mongo Driver](https://github.com/vapor/mongo-driver): MongoKitten driver for Fluent.
- [Mongo Provider](https://github.com/vapor/mongo-provider): MongoKitten provider for Vapor.
- [MainecoonVapor](https://github.com/OpenKitten/MainecoonVapor): MongoKitten ORM for Vapor.
- [Redbird](https://github.com/vapor/redbird): Pure-Swift Redis client implemented from the original protocol spec..
- [Redis Provider](https://github.com/vapor/redis-provider): Redis cache provider for Vapor.
- [Kitura Provider](https://github.com/vapor/kitura-provider): Use IBM's Kitura HTTP server in Vapor.
- [SwiftyBeaver](https://github.com/SwiftyBeaver/SwiftyBeaver-Vapor): Adds the powerful logging of SwiftyBeaver to Vapor.
- [APNS](https://github.com/matthijs2704/vapor-apns): Simple APNS Library for Vapor (Swift).
- [VaporFCM](https://github.com/mdab121/vapor-fcm): Simple FCM (iOS + Android Push Notifications) library built for Vapor in Swift.
- [JWT](https://github.com/siemensikkema/vapor-jwt): JWT implementation for Vapor.
- [VaporS3Signer](https://github.com/JustinM1/VaporS3Signer): Generate V4 Auth Header/Pre-Signed URL for AWS S3 REST API
- [Flock](https://github.com/jakeheis/Flock): Automated deployment of Swift projects to servers
- [VaporFlock](https://github.com/jakeheis/VaporFlock): Use Flock to deploy Vapor applications
- [VaporForms](https://github.com/bygri/vapor-forms): Brings simple, dynamic and re-usable web form handling to Vapor.
- [Jobs](https://github.com/BrettRToomey/Jobs): A minimalistic job/background-task system for Swift.
- [Heimdall](https://github.com/himani93/heimdall): An easy to use HTTP request logger.
## Authors
[Tanner Nelson](mailto:tanner@qutheory.io), [Logan Wright](mailto:logan@qutheory.io),and the hundreds of members of Vapor.

View File

@ -1,84 +0,0 @@
# Vapor 文档
[![Stack Overflow](https://img.shields.io/stackexchange/stackoverflow/t/vapor.svg)](http://stackoverflow.com/questions/tagged/vapor)
这是 Vapor 的说明文档, Vapor 是一个可以在 iOS, macOS 及 Ubuntu 上执行的 Web framework以及其他相关的组件。
Vapor 是一个在 Swift 上很受欢迎的 Web framework。它提供了清晰易用的 API 及许多方便的基础功能,方便我们用它建立网站或是后台。
我们可以在 [Vapor's GitHub](https://github.com/vapor/vapor) 查看源码及说明文档。
阅读 [繁体中文](https://github.com/vapor/documentation/1.5/README.zh-hant.md)
阅读 [Spanish](https://github.com/vapor/documentation/1.5/README.es.md)
阅读 [English](https://github.com/vapor/documentation/1.5/README.md)
## 说明文档
可在 [GitHub](https://github.com/vapor/documentation) 上浏览说明文档,特別是 markdown 文档(后缀名为 .md 的文档)。或是查看 [GitHub Pages](https://vapor.github.io/documentation/) 上的文件。
## 组件
以下是 Vapor 提供的组件及模板(我们也可以不通过 Vapor而直接使用它们。)
- [Vapor](https://github.com/vapor/vapor): Swift 上最常用到 web framework。
- Auth: 使用认证及存储控制(persistance)。
- Sessions: 建立在 cookie 机制上安全、短暂的资料存储。
- Cookies: HTTP cookies.
- Routing: 可通过变量确定(type-safe)的参数来设定路径。
- [Fluent](https://github.com/vapor/fluent): 用来操作 SQL 或 NoSQL 资料库。
- [Engine](https://github.com/vapor/engine): 传输的核心层。
- HTTP: HTTP 用戶端及服务端。
- URI: URI 的解析及组成。
- WebSockets: TCB 连线双向沟通管道。
- SMTP: 通过 Sendgrid 及 Gmail 发送邮件。
- [Leaf](https://github.com/vapor/leaf): 一种可扩展的脚本语言(extensible templating language)。(注: 可以用来建立使用界面。)
- [JSON](https://github.com/vapor/json): 用 [Jay JSON]((https://github.com/dantoml/jay)) 解析工具生成 Vapor 物件。
- [Console](https://github.com/vapor/console): 用來处理 console 的输入、输出指令的 Swift 工具。
- [TLS](https://github.com/vapor/tls): 用來处理 CLibreSSL 的新型 TLS 的 Swift 工具。
- [Crypto](https://github.com/vapor/crypto): 在 LibreSSL 及 Swift 上进行加密的工具。
- Digests: 哈希与认证。
- Ciphers: 编码及解码。
- Random: 安全的随机数。
- BCrypt: 完全用 Swift 所写。
- [Node](https://github.com/vapor/node): 可以轻易地进行类型转换。
- [Polymorphic](https://github.com/vapor/polymorphic): 如同 JSON 一般可以轻易调用数据。
- [Path Indexable](https://github.com/vapor/path-indexable): 如同 JSON 一样可以用来处理复杂的资料结构。
- [Core](https://github.com/vapor/core): 核心扩展,类型别名和一些常见任务的功能。
- [Socks](https://github.com/vapor/socks): 将 C 语言的 Socket API 包装成 Swift 语言。
## 可组合使用的框架
以下是可以和 Vapor 同时使用的组件列表。(译:原文里这里还有个东西叫 Provider是一种 protocol让我们可以在 Vapor 中像第三方组件一样使用。)
- [MySQL](https://github.com/vapor/mysql): 可通过 Swift 操作 MySQL 的框架。
- [MySQL Driver](https://github.com/vapor/mysql-driver): 通过 Fluent 操作 MySQL 的框架。
- [MySQL Provider](https://github.com/vapor/mysql-provider): 让 MySQL 可以在 Vapor 上运作的 provider。
- [SQLite](https://github.com/vapor/sqlite): 可通过 Swift 操作 SQLite 3 的框架。
- [SQLite Driver](https://github.com/vapor/sqlite-driver): 通过 Fluent 操作 SQLite 的工具。
- [SQLite Provider](https://github.com/vapor/sqlite-provider): 让 SQLite 可以在 Vapor 上运作的 provider。
- [PostgreSQL](https://github.com/vapor/postgresql): 用 Swift 操作 PostgreSQL 的工具。
- [PostgreSQL Driver](https://github.com/vapor/postgresql-driver): 用 Fluent 操作 PostgreSQL 的框架。
- [PostgreSQL Provider](https://github.com/vapor/postgresql-provider): 让 PostgreSQL 可以运行在 Vapor 上的 provider。
- [MongoKitten*](https://github.com/OpenKitten/MongoKitten): 用 Swift 写的 MongoDB driver。
- [Mongo Driver](https://github.com/vapor/mongo-driver): Fluent 用的 MongoKitten driver。
- [Mongo Provider](https://github.com/vapor/mongo-provider): Vapor 用的 MongoKitten provider.
- [MainecoonVapor](https://github.com/OpenKitten/MainecoonVapor): Vapor 的 MongoKitten 组件关联管理。
- [Redbird](https://github.com/vapor/redbird): 遵循原始协定创造的 Swift Redis client 端。
- [Redis Provider](https://github.com/vapor/redis-provider): Vapor 的 Redis cache provider。
- [Kitura Provider](https://github.com/vapor/kitura-provider): 在 Vapor 中使用 IBM 的 Kitura HTTP Server。
- [SwiftyBeaver](https://github.com/SwiftyBeaver/SwiftyBeaver-Vapor): 在 Vapor 中使用 SwiftBeaver 的框架。(译注: 像强化版的 NSLog() 或 print())
- [APNS](https://github.com/matthijs2704/vapor-apns): 用来操作 Apple 推送的工具。
- [VaporFCM](https://github.com/mdab121/vapor-fcm): 用于发送FCM通知的简单库。
- [JWT](https://github.com/siemensikkema/vapor-jwt): 让我们可以设定一些规则以取得特定资源的工具。
- [VaporS3Signer](https://github.com/JustinM1/VaporS3Signer): 用来产生 HTTP request 的 headers 及已经签证过的 URL用来 request AWS S3 的 REST API。
- [Flock](https://github.com/jakeheis/Flock): 自动将 Swift 专案发布上主机。
- [VaporFlock](https://github.com/jakeheis/VaporFlock): 利用 Flock 发布 Vapor applications。
- [VaporForms](https://github.com/bygri/vapor-forms): 让我们在处理前端送来的 form request 时可以轻松一点的框架。
- [Jobs](https://github.com/BrettRToomey/Jobs): 在某个特定的时间点执行某些程式码的框架。
- [Heimdall](https://github.com/himani93/heimdall): 用来将收到的 http request 记录到某个档案的框架,且可以用试算表类型(ex: excel, google sheets)的软件开启。
## 作者
[Tanner Nelson](mailto:tanner@qutheory.io), [Logan Wright](mailto:logan@qutheory.io), [Jinxiansen](mailto:hi@jinxiansen.com), 以及其他上百位 Vapor 的贡献者们。

View File

@ -1,84 +0,0 @@
# Vapor Documentation
[![Stack Overflow](https://img.shields.io/stackexchange/stackoverflow/t/vapor.svg)](http://stackoverflow.com/questions/tagged/vapor)
這是 Vapor 的說明文件, Vapor 是一個可以在 iOS, macOS 及 Ubuntu 上執行的 Web framework以及其他相關的套件。
Vapor 是一個在 Swift 上很受歡迎的 Web framework。它提供了清楚易用的 API 及許多方便的基礎功能,方便我們用它建立網站或是後台。
我們可以在 [Vapor's GitHub](https://github.com/vapor/vapor) 查看原始碼及說明文件。
閱讀 [English](https://github.com/vapor/documentation/1.5/README.md)
閱讀 [Spanish](https://github.com/vapor/documentation/1.5/README.es.md)
閱讀 [简体中文](https://github.com/vapor/documentation/1.5/README.zh-cn.md)
## 如何閱讀說明文件
在 [GitHub](https://github.com/vapor/documentation) 上瀏覽每個資料夾,特別是 markdown 檔(副檔名為 .md 的檔案)。或是看 [GitHub Pages](https://vapor.github.io/documentation/) 上的文件。
## 套件
以下是 Vapor 提供的套件及模組(我們也可以直接使用它們,不透過 Vapor。)
- [Vapor](https://github.com/vapor/vapor): Swift 上最常被使用到的 web framework。
- Auth: 使用者的認證及存續控制(persistance)。
- Sessions: 建立在 cookie 機制上安全、短暫的資料儲存。
- Cookies: HTTP cookies.
- Routing: 可透過變數類型確定(type-safe)的參數設定來設定路徑。
- [Fluent](https://github.com/vapor/fluent): 用來操作 SQL 或 NoSQL 資料庫。
- [Engine](https://github.com/vapor/engine): 傳輸的核心層。
- HTTP: HTTP 用戶端及主機端。
- URI: URI 的分解及組成。
- WebSockets: 在一個 TCB 連線中進行雙向的溝通管道。
- SMTP: 透過 Sendgrid 及 Gmail 發送郵件。
- [Leaf](https://github.com/vapor/leaf): 一種可擴張的樣本語言(extensible templating language)。(譯註: 這可以用來建立使用者介面。)
- [JSON](https://github.com/vapor/json): 用 [Jay JSON]((https://github.com/dantoml/jay)) 解析工具產生Vapor物件。
- [Console](https://github.com/vapor/console): 用來處理 console 的輸入、輸出及指令的 Swift 工具。
- [TLS](https://github.com/vapor/tls): 用來處理 CLibreSSL 的新型 TLS 的 Swift 工具。
- [Crypto](https://github.com/vapor/crypto): 在 LibreSSL 及 Swift 上進行加密的工具。
- Digests: 無論有沒有認證(authentication)都可以進行雜湊(hash)。
- Ciphers: 編碼及解碼。
- Random: 安全的隨機性。
- BCrypt: 完全用 Swift 實作。
- [Node](https://github.com/vapor/node): 可以輕易地進行類型轉換的資料結構。
- [Polymorphic](https://github.com/vapor/polymorphic): 如同 JSON 一般可以輕易取用資料的語法。
- [Path Indexable](https://github.com/vapor/path-indexable): 如同 JSON 一樣可以用來處理複雜的資料結構。
- [Core](https://github.com/vapor/core): 主要的 extension 群,例如: 變數類型的重新命名、在許多地方都會被使用的 function 等。
- [Socks](https://github.com/vapor/socks): 將 C 語言的 Socket API 包裝成 Swift 語言。
## 可合併使用的套件
以下是可以和 Vapor 合併運用的套件列表。(譯註:原文裡這裡還有個東西叫 Provider那是一種 protocol讓我們可以掛到 Vapor 中如同第三方元件一樣使用。)
- [MySQL](https://github.com/vapor/mysql): 可透過 Swift 操作 MySQL 的套件。
- [MySQL Driver](https://github.com/vapor/mysql-driver): 透過 Fluent 操作 MySQL 的套件。
- [MySQL Provider](https://github.com/vapor/mysql-provider): 讓 MySQL 可以在 Vapor 上運作的 provider。
- [SQLite](https://github.com/vapor/sqlite): 可透過 Swift 操作 SQLite 3 的套件。
- [SQLite Driver](https://github.com/vapor/sqlite-driver): 透迥 Fluent 操作 SQLite 的工具。
- [SQLite Provider](https://github.com/vapor/sqlite-provider): 讓 SQLite 可以在 Vapor 上運作的 provider。
- [PostgreSQL](https://github.com/vapor/postgresql): 用 Swift 操作 PostgreSQL 的工具。
- [PostgreSQL Driver](https://github.com/vapor/postgresql-driver): 用 Fluent 操作 PostgreSQL 的套件。
- [PostgreSQL Provider](https://github.com/vapor/postgresql-provider): 讓 PostgreSQL 可以運作在 Vapor 上的 provider。
- [MongoKitten*](https://github.com/OpenKitten/MongoKitten): 用 Swift 寫的 MongoDB driver。
- [Mongo Driver](https://github.com/vapor/mongo-driver): Fluent 用的 MongoKitten driver。
- [Mongo Provider](https://github.com/vapor/mongo-provider): Vapor 用的 MongoKitten provider.
- [MainecoonVapor](https://github.com/OpenKitten/MainecoonVapor): Vapor 的 MongoKitten 物件關聯管理。
- [Redbird](https://github.com/vapor/redbird): 遵循原始協定的規格實作出來的 Swift Redis client 端。
- [Redis Provider](https://github.com/vapor/redis-provider): Vapor 的 Redis cache provider。
- [Kitura Provider](https://github.com/vapor/kitura-provider): 在 Vapor 中使用 IBM 的 Kitura HTTP Server。
- [SwiftyBeaver](https://github.com/SwiftyBeaver/SwiftyBeaver-Vapor): 在 Vapor 中使用 SwiftBeaver 的套件。(譯註: 就像強化版的 NSLog() 或 print())
- [APNS](https://github.com/matthijs2704/vapor-apns): 用來操作 Apple 推播的工具。
- [VaporFCM](https://github.com/mdab121/vapor-fcm): 用于发送FCM通知的简单库。
- [JWT](https://github.com/siemensikkema/vapor-jwt): 讓我們可以設定一些規則以取得特定資源的工具。
- [VaporS3Signer](https://github.com/JustinM1/VaporS3Signer): 用來產生 HTTP request 的 headers 及已經簽證過的 URL用來 request AWS S3 的 REST API。
- [Flock](https://github.com/jakeheis/Flock): 自動將 Swift 專案發佈上主機。
- [VaporFlock](https://github.com/jakeheis/VaporFlock): 利用 Flock 發佈 Vapor applications。
- [VaporForms](https://github.com/bygri/vapor-forms): 讓我們在處理前端送來的 form request 時可以輕鬆一點的套件。
- [Jobs](https://github.com/BrettRToomey/Jobs): 在某個特定的時間點執行某些程式碼的套件。
- [Heimdall](https://github.com/himani93/heimdall): 用來將收到的 http request 記錄到某個檔案的套件,且這個寫好的檔可以用試算表類型(ex: excel, google sheets)的軟體開啟。
## 作者們
[Tanner Nelson](mailto:tanner@qutheory.io), [Logan Wright](mailto:logan@qutheory.io),以及其他上百位 Vapor 的貢獻者們。

View File

@ -1,89 +0,0 @@
---
currentMenu: auth-middleware
---
# Middleware
`AuthMiddleware` is at the core of adding authorization to your project. It is responsible for initializing dependencies, checking credentials, and handling sessions.
## Create
Once you have something that conforms to `Auth.User`, you can create an `AuthMiddleware`. Let's assume we have a class `User` that conforms to `Auth.User`.
> Note: You may need to include a module name before `User` to disambiguate.
```swift
import Auth
let auth = AuthMiddleware(user: User.self)
```
Creating the `AuthMiddleware` can be that simple, or you can customize it with additional initialization arguments.
### Cookie
Customize the type of cookie the `AuthMiddleware` creates by passing a `CookieFactory`.
```swift
let auth = AuthMiddleware(user: User.self) { value in
return Cookie(
name: "vapor-auth",
value: value,
expires: Date().addingTimeInterval(60 * 60 * 5), // 5 hours
secure: true,
httpOnly: true
)
}
```
### Cache
A custom `CacheProtocol` can be passed as well. The `MemoryCache` used by default is not persisted between server restarts and does not allow for sharing between multiple running instances.
```swift
import VaporRedis
let redis = RedisCache()
let auth = AuthMiddleware(user: User.self, cache: redis)
```
> Note: This example uses the [redis-provider](https://github.com/vapor/redis-provider) package.
### Realm
To customize the `AuthMiddleware` even further, you can use a custom `Realm`. The `Realm` takes the responsibility of registering and authenticating the user away from the `Auth.User` protocol.
```swift
let facebook = FacebookRealm()
let auth = AuthMiddleware(user: User.self, realm: facebook)
```
> Note: `FacebookRealm` is hypothetical.
## Add
Once you've created the `AuthMiddleware`, you can add it to the `Droplet`.
```swift
let drop = Droplet()
drop.middleware.append(auth)
```
> Note: If you'd like to enable or disable the middleware based on config files, check out [middleware](../guide/middleware.md).
### Sharing Cache
If you'd like the `Droplet` and the `AuthMiddleware` to share the same `CacheProtocol`, pass the same instance to both.
```
import Vapor
import VaporRedis
let redis = RedisCache()
let auth = AuthMiddleware(user: User.self, cache: redis)
let drop = Droplet()
drop.cache = redis
drop.middleware.append(auth)
```

View File

@ -1,33 +0,0 @@
---
currentMenu: auth-protect
---
# Protect
Once the `AuthMiddleware` has been enabled, you can use `ProtectMiddleware` to prevent certain routes from being accessed without authorization.
## Create
To create a `ProtectMiddleware`, you must give it the error to throw in case authorization fails.
```swift
let error = Abort.custom(status: .forbidden, message: "Invalid credentials.")
let protect = ProtectMiddleware(error: error)
```
Here we pass it a simple 403 response.
## Route Group
Once the middleware has been created, you can add it to route groups. Learn more about middleware and routing in [route groups](../routing/group.md).
```
drop.grouped(protect).group("secure") { secure in
secure.get("about") { req in
let user = try req.user()
return user
}
}
```
Visiting `GET /secure/about` will return the authorized user, or an error if no user is authorized.

View File

@ -1,161 +0,0 @@
---
currentMenu: auth-request
---
# Request
The `auth` property on `Request` lets you authenticate users and also provides some convenience methods for accessing common authorization headers.
## Authorization
The authorization header is a great place to send credentials from a client.
```
Authorization: xxxxxxxxxx
```
You can access the authorization header through `req.auth.header`. Two common patterns are basic and bearer.
### Basic
Basic authorization consists of a username and password concatenated into a string and base64 encoded.
```
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l
```
This is what an example header looks like. You can read more about basic auth on [wikipedia](https://en.wikipedia.org/wiki/Basic_access_authentication).
Below is how you access this header using `req.auth`.
```swift
guard let credentials = req.auth.header?.basic else {
throw Abort.badRequest
}
```
The basic header returns an `APIKey` credential.
```
class APIKey: Credentials {
let id: String
let secret: String
}
```
### Bearer
Another common method is bearer which consists of a single API key.
```
Authorization: Bearer apikey123
```
It is accessed similarly to the basic header and returns an `AccessToken` credential.
```
class AccessToken: Credentials {
let string: String
}
```
### Raw
To access the raw authorization header, use `req.auth.header?.header`.
## Credentials
Both Basic and Bearer return something that conforms to `Credentials`. You can always create a custom `Credentials` object for authentication by conforming your own class to `Credentials` or by manually creating an `APIKey`, `AccessToken`, or `Identifier`.
```swift
let key = AccessToken(string: "apikey123")
```
### Input
You can also create credentials from form or JSON data.
```swift
guard
let username = req.data["username"]?.string,
let password = req.data["password"]?.string
else {
throw Abort.badRequest
}
let key = APIKey(id: username, secret: password)
```
## Login
Once you have some object that conforms to `Credentials`, you can try to login the user.
```swift
try req.auth.login(credentials)
```
If this call succeeds, the user is logged in and a session has been started. They will stay logged in as long as their cookie is valid.
### Authenticate
Logging in calls the `authenticate` method on `Auth.User` model you supplied to the `AuthMiddleware`. Make sure you add support for all the credential types you may want to use.
> Note: If you used a custom Realm, it will be called instead.
### Identifier
Another important credential type is the `Identifier` type. This is used by Vapor when fetching the `User` object from the `vapor-auth` cookie. It is also a convenient way to log a user in manually.
```swift
static func authenticate(credentials: Credentials) throws -> Auth.User {
switch credentials {
...
case let id as Identifier:
guard let user = try User.find(id.id) else {
throw Abort.custom(status: .badRequest, message: "Invalid identifier.")
}
return user
...
}
}
```
Adding the `Identifier` case for `Credentials` is easy, just look up the user by the identifier.
```swift
let id = Identifier(id: 42)
try req.auth.login(id)
```
Now you can manually log users in with just their identifiers.
### Ephemeral
If you just want to log the user in for a single request, disable persistance.
```swift
req.auth.login(credentials, persist: false)
```
> Note: Supporting `Identifier` credentials is required for persisted authentication to work properly.
## User
By default, `request.auth.user()` returns the authorized `Auth.User`. This will need to be casted to your internal `User` type for use.
Adding a convenience method on `Request` is a great way to simplify this.
```swift
extension Request {
func user() throws -> User {
guard let user = try auth.user() as? User else {
throw Abort.custom(status: .badRequest, message: "Invalid user type.")
}
return user
}
}
```
Now you can access your `User` type with `try req.user()`.

View File

@ -1,120 +0,0 @@
---
currentMenu: auth-user
---
# Auth
Authentication and authorization is focused around the `Auth.User` protocol. Authentication is analagous to asking: "Who is this?", while authorization is analagous to asking: "What can they do?". Vapor includes an extensible authentication system that you can use as a base for more sophisticated authorization.
> Note: An [auth-example](https://github.com/vapor/auth-example) project is available on GitHub.
## User Protocol
Any type can conform to the `Auth.User` protocol, but they are commonly added onto Fluent `Model`s.
```swift
import Vapor
import Auth
final class User: Model {
var id: Node?
var name: String
...
}
extension User: Auth.User {
static func authenticate(credentials: Credentials) throws -> Auth.User {
}
static func register(credentials: Credentials) throws -> Auth.User {
}
}
```
Here is an example `User` class with the `Auth.User` protocol requirements stubbed. Note that the name of our class and the protocol are the same. This is why we use the `Auth.` prefix to differentiate the protocol from the `Auth` module from our `User` class.
### Authenticate
A user is authenticated when a set of credentials is passed to the static `authenticate` method and the matching user is returned.
#### Credentials
```swift
protocol Credentials { }
```
The credentials protocol is an empty protocol that any type can conform to. This gives great flexibility to your authentication model, but also requires that you properly handle the case of unsupported credential types.
#### Access Token
One of the simplest credential types included is `AccessToken`. It carries a `String` based token that will be used to authenticate the user.
Let's look at how we might support the access token type.
```swift
static func authenticate(credentials: Credentials) throws -> Auth.User {
switch credentials {
case let accessToken as AccessToken:
guard let user = try User.query().filter("access_token", accessToken.string).first() else {
throw Abort.custom(status: .forbidden, message: "Invalid access token.")
}
return user
default:
let type = type(of: credentials)
throw Abort.custom(status: .forbidden, message: "Unsupported credential type: \(type).")
}
}
```
The first step is to cast the credentials to the type we want to support--in this case, `AccessToken`. If we do not have an access token, we will inform the client that the credentials are invalid.
Once we have the access token, we will use it to query the `User` model for an entry with a matching access token. This is assuming the `users` table or collection has the access tokens stored on it. You may opt to store them somewhere else.
Once we have found the user associated with the supplied access token, we simply return it.
#### Identifier
Vapor uses the `Identifier` credential type internally to lookup users from sessions. You can read more in the [Request](request.md) section.
### Register
Similar to the authenticate method, the register method takes credentials. But instead of fetching the user from the data store, it provides a convenient way to create the user. You are not required to register your users through this method.
## Example
Here is an example of a User that supports multiple credentials.
```swift
extension User: Auth.User {
static func authenticate(credentials: Credentials) throws -> Auth.User {
let user: User?
switch credentials {
case let id as Identifier:
user = try User.find(id.id)
case let accessToken as AccessToken:
user = try User.query().filter("access_token", accessToken.string).first()
case let apiKey as APIKey:
user = try User.query().filter("email", apiKey.id).filter("password", apiKey.secret).first()
default:
throw Abort.custom(status: .badRequest, message: "Invalid credentials.")
}
guard let u = user else {
throw Abort.custom(status: .badRequest, message: "User not found.")
}
return u
}
static func register(credentials: Credentials) throws -> Auth.User {
...
}
}
```
> Note: Try not to store passwords. If you must, hash and salt them.

View File

@ -1,196 +0,0 @@
template:
directory: template
title: Vapor Documentation
subTitle: A web framework and server for Swift that works on macOS and Ubuntu.
baseUrl: /1.5
menu:
sections:
getting-started:
name: Getting Started
items:
getting-started-install-swift-3-macos:
text: "Install Swift 3: macOS"
relativeUrl: getting-started/install-swift-3-macos.html
getting-started-install-swift-3-ubuntu:
text: "Install Swift 3: Ubuntu"
relativeUrl: getting-started/install-swift-3-ubuntu.html
getting-started-install-toolbox:
text: Install Toolbox
relativeUrl: getting-started/install-toolbox.html
getting-started-hello-world:
text: Hello, World
relativeUrl: getting-started/hello-world.html
getting-started-manual:
text: Manual
relativeUrl: getting-started/manual.html
getting-started-xcode:
text: Xcode
relativeUrl: getting-started/xcode.html
guide:
name: Guide
items:
guide-droplet:
text: Droplet
relativeUrl: guide/droplet.html
guide-folder-structure:
text: Folder Structure
relativeUrl: guide/folder-structure.html
guide-json:
text: JSON
relativeUrl: guide/json.html
guide-config:
text: Config
relativeUrl: guide/config.html
guide-views:
text: Views
relativeUrl: guide/views.html
guide-leaf:
text: Leaf
relativeUrl: guide/leaf.html
guide-controllers:
text: Controllers
relativeUrl: guide/controllers.html
guide-middleware:
text: Middleware
relativeUrl: guide/middleware.html
guide-validation:
text: Validation
relativeUrl: guide/validation.html
guide-provider:
text: Provider
relativeUrl: guide/provider.html
guide-sessions:
text: Sessions
relativeUrl: guide/sessions.html
guide-hash:
text: Hash
relativeUrl: guide/hash.html
guide-commands:
text: Commands
relativeUrl: guide/commands.html
routing:
name: Routing
items:
routing-basic:
text: Basic
relativeUrl: routing/basic.html
routing-parameters:
text: Route Parameters
relativeUrl: routing/parameters.html
routing-query-parameters:
text: Query Parameters
relativeUrl: routing/query-parameters.html
routing-group:
text: Group
relativeUrl: routing/group.html
routing-collection:
text: Collection
relativeUrl: routing/collection.html
fluent:
name: Fluent
items:
fluent-driver:
text: Driver
relativeUrl: fluent/driver.html
fluent-model:
text: Model
relativeUrl: fluent/model.html
fluent-query:
text: Query
relativeUrl: fluent/query.html
fluent-relation:
text: Relation
relativeUrl: fluent/relation.html
auth:
name: Auth
items:
auth-user:
text: User
relativeUrl: auth/user.html
auth-middleware:
text: Middleware
relativeUrl: auth/middleware.html
auth-request:
text: Request
relativeUrl: auth/request.html
auth-protect:
text: Protect
relativeUrl: auth/protect.html
http:
name: HTTP
items:
http-request:
text: Request
relativeUrl: http/request.html
http-response:
text: Response
relativeUrl: http/response.html
http-body:
text: Body
relativeUrl: http/body.html
http-response-representable:
text: ResponseRepresentable
relativeUrl: http/response-representable.html
http-responder:
text: Responder
relativeUrl: http/responder.html
http-client:
text: Client
relativeUrl: http/client.html
http-server:
text: Server
relativeUrl: http/server.html
http-cors:
text: CORS
relativeUrl: http/cors.html
web-sockets:
name: WebSockets
items:
websockets-droplet:
text: Droplet
relativeUrl: websockets/droplet.html
websockets-custom:
text: Custom
relativeUrl: websockets/custom.html
testing:
name: Testing
items:
testing-modules:
text: Modules
relativeUrl: testing/modules.html
testing-basic:
text: Basic
relativeUrl: testing/basic.html
deploy:
name: Deploy
items:
deploy-nginx:
text: Nginx
relativeUrl: deploy/nginx.html
deploy-supervisor:
text: Supervisor
relativeUrl: deploy/supervisor.html
versions:
name: Version (1.5)
items:
1_5:
text: '1.5'
relativeUrl: switch/1_5.html
2_0:
text: '2.0'
relativeUrl: switch/2_0.html
3_0:
text: '3.0'
relativeUrl: switch/3_0.html
4_0:
text: '4.0'
relativeUrl: switch/4_0.html

View File

@ -1,149 +0,0 @@
---
currentMenu: deploy-nginx
---
# Deploying with Nginx
Nginx is an extremely fast, battle tested, and easy-to-configure HTTP server and proxy. While Vapor supports directly serving HTTP requests with or without TLS, proxying behind Nginx can provide increased performance, security, and ease-of-use.
> Note: We recommend proxying Vapor HTTP servers behind Nginx.
## Overview
What does it mean to proxy an HTTP server? In short, a proxy acts as a middleman between the public internet and your HTTP server. Requests come to the proxy and then it sends them to Vapor.
An important feature of this middleman proxy is that it can alter or even redirect the requests. For instance, the proxy can require that the client use TLS (https), rate limit requests, or even serve public files without talking to your Vapor application.
![nginx-proxy](https://cloud.githubusercontent.com/assets/1342803/20184965/5d9d588a-a738-11e6-91fe-28c3a4f7e46b.png)
### More Detail
The default port for receiving HTTP requests is port `80` (and `443` for HTTPS). When you bind a Vapor server to port `80`, it will directly receive and respond to the HTTP requests that come to your server. When adding a proxy like Nginx, you bind Vapor to an internal port, like port `8080`.
> Note: Ports greater than 1024 do not require `sudo` to bind.
When Vapor is bound to a port besides `80` or `443`, it will not be accessible to the outside internet. You then bind Nginx to port `80` and configure it to route requests to your Vapor server bound at port `8080` (or whichever port you've chosen).
And that's it. If Nginx is properly configured, you will see your Vapor app responding to requests on port `80`. Nginx proxies the requests and responses invisibly.
## Install Nginx
The first step is installing Nginx. One of the great parts of Nginx is the tremendous amount of community resources and documentation surrounding it. Because of this, we will not go into great detail here about installing Nginx as there is almost definitely a tutorial for your specific platform, OS, and provider.
Tutorials:
- [How To Install Nginx on Ubuntu 14.04 LTS](https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-14-04-lts)
- [How To Install Nginx on Ubuntu 16.04](https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-16-04)
- [How to Deploy Nginx on Heroku](https://blog.codeship.com/how-to-deploy-nginx-on-heroku/)
- [How To Run Nginx in a Docker Container on Ubuntu 14.04](https://www.digitalocean.com/community/tutorials/how-to-run-nginx-in-a-docker-container-on-ubuntu-14-04)
### APT
Nginx can be installed through APT.
```sh
sudo apt-get update
sudo apt-get install nginx
```
Check whether Nginx was installed correctly by visiting your server's IP address in a browser
```sh
http://server_domain_name_or_IP
```
### Service
Ther service an be started or stopped.
```sh
sudo service nginx stop
sudo service nginx start
sudo service nginx restart
```
## Booting Vapor
Nginx can be started an stopped with the `sudo service nginx ...` commands. You will need something similar to start and stop your Vapor server.
There are many ways to do this, and they depend on which platform you are deploying to. Check out the [Supervisor](supervisor.md) instructions to add commands for starting and stopping your Vapor app.
## Configure Proxy
The configuration files for enabled sites can be found in `/etc/nginx/sites-enabled/`.
Create a new file or copy the example template from `/etc/nginx/sites-available/` to get started.
Here is an example configuration file for a Vapor project called `Hello` in the home directory.
```sh
server {
server_name hello.com;
listen 80;
root /home/vapor/Hello/Public/;
location @proxy {
proxy_pass http://127.0.0.1:8080;
proxy_pass_header Server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_header Server;
proxy_connect_timeout 3s;
proxy_read_timeout 10s;
}
}
```
This configuration file assumes the `Hello` project binds to port `8080` when started in production mode.
### Serving Files
Nginx can also serve public files without asking your Vapor app. This can improve performance by freeing up the Vapor process for other tasks under heavy load.
```sh
server {
...
# Serve all public/static files via nginx and then fallback to Vapor for the rest
try_files $uri @proxy;
location @proxy {
...
}
}
```
### TLS
Adding TLS is relatively straightforward as long as the certificates have been properly generated. To generate TLS certificates for free, check out [Let's Encrypt](https://letsencrypt.org/getting-started/).
```sh
server {
...
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/hello.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/hello.com/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security max-age=15768000;
...
location @proxy {
...
}
}
```
The configuration above are the relatively strict settings for TLS with Nginx. Some of the settings here are not required, but enhance security.

View File

@ -1,65 +0,0 @@
---
currentMenu: deploy-supervisor
---
# Supervisor
[Supervisor](http://supervisord.org) is a process control system that makes it easy to start, stop, and restart your Vapor app.
## Install
```sh
sudo apt-get update
sudo apt-get install supervisor
```
## Configure
Each Vapor app on your server should have its own configuration file. For an example `Hello` project, the configuration file would be located at `/etc/supervisor/conf.d/hello.conf`
```sh
[program:hello]
command=/home/vapor/hello/.build/release/App serve --env=production
directory=/home/vapor/hello/
user=www-data
stdout_logfile=/var/log/supervisor/%(program_name)-stdout.log
stderr_logfile=/var/log/supervisor/%(program_name)-stderr.log
```
As specified in our configuration file the `Hello` project is located in the home folder for the user `vapor`. Make sure `directory` points to the root directory of your project where the `Config/` folder is.
The `--env=production` flag will disable verbose logging and prioritize the `Config/production` sub folder of your configuration files.
### Environment
You can export variables to your Vapor app with supervisor.
```sh
environment=PORT=8123
```
Exported variables can be used in Vapor's configuration files with the `$` prefix.
`Config/production/servers.json `
```json
{
"my-server": {
"port": "$PORT"
}
}
```
The above config file will start a server named `my-server` on the port number exported by supervisor. This is a great way to control how Vapor starts from the supervisor config scripts. Feel free to name the server whatever you like.
## Start
You can now load and start your app.
```sh
supervisorctl reread
supervisorctl add hello
supervisorctl start hello
```
> Note: The `add` command may have already started your app.

View File

@ -1,52 +0,0 @@
---
currentMenu: fluent-driver
---
# Driver
Drivers are what power Fluent under the hood. Fluent comes with a memory driver by default and there are many providers for databases like MySQL, SQLite, Mongo, PostgreSQL, and more available as providers.
![Drivers and Providers](https://cloud.githubusercontent.com/assets/1342803/17418823/73f1d1d2-5a68-11e6-9bed-90f42ce7781d.png)
This graphic shows the relation between Drivers and Providers using MySQL as an example. This distinction allows Fluent to be used independently from Vapor.
If you want to use Fluent without Vapor, you will import Drivers into your package. If you are using Vapor, you will import Providers.
Search GitHub for:
- [Fluent Drivers](https://github.com/vapor?utf8=✓&q=-driver)
- [Vapor Providers](https://github.com/vapor?utf8=✓&q=-provider)
Not all drivers have providers yet, and not all drivers or providers are up to date with the latest Vapor 1.0. This is a great way to contribute!
## Creating a Driver
Fluent is a powerful, database agnostic package for persisting your models. It was designed from the beginning to work with both SQL and NoSQL databases alike.
Any database that conforms to `Fluent.Driver` will be able to power the models in Fluent and Vapor.
The protocol itself is fairly simple:
```swift
public protocol Driver {
var idKey: String { get }
func query<T: Entity>(_ query: Query<T>) throws -> Node
func schema(_ schema: Schema) throws
func raw(_ raw: String, _ values: [Node]) throws -> Node
}
```
### ID Key
The ID key will be used to power functionality like `User.find()`. In SQL, this is often `id`. In MongoDB, it is `_id`.
### Query
This method will be called for every query made by Fluent. It is the drivers job to properly understand all of the properties on `Query` and return the desired rows, document, or other data as represented by `Node`.
### Schema
The schema method will be called before the database is expected to accept queries for a schema. For some NoSQL databases like MongoDB, this can be ignored. For SQL, this is where `CREATE` and other such commands should be called according to `Schema`.
### Raw
This is an optional method that can be used by any Fluent driver that accepts string queries. If your database does not accept such queries, an error can be thrown.

View File

@ -1,39 +0,0 @@
---
currentMenu: fluent-fixtures
---
# Fixtures
When you want to prepopulate a database with existing data, you will need to create one or several fixtures. For example, in your table `users`, it's very common to create admin account and a test account just after the creation of the table. The following snippets of code show you how to deal with it.
In a separate file (Database+Fixture.swift):
```swift
extension Database {
func insertFixtures<T: Entity, S: Sequence>(_ data: S) throws where S.Iterator.Element == T {
let context = DatabaseContext(self)
try data.forEach { model in
let query = Query<T>(self)
query.action = .create
query.data = try model.makeNode(context: context)
try driver.query(query)
}
}
}
```
In your model User.swift file:
```swift
static func prepare(_ database: Database) throws {
try database.create("users") { users in
users.id()
users.string("email")
users.string("password")
}
let seedData: [AppUser] = [
try User(email: "admin@admin.com", rawPassword: "Def4ultPassword?!"),
try User(email: "test@test.com", rawPassword: "Def4ultPassword?!")
]
try database.insertFixtures(seedData)
}
```

View File

@ -1,246 +0,0 @@
---
currentMenu: fluent-model
---
# Model
`Model` is the base protocol for any of your application's models, especially those you want to persist.
> `Model` is only available in Vapor, the Fluent equivalent is `Entity`
## Example
Let's create a simple `User` model.
```swift
final class User {
var name: String
init(name: String) {
self.name = name
}
}
```
The first step to conforming to `Model` is to import Vapor and Fluent.
```swift
import Vapor
import Fluent
```
Then add the conformance to your class.
```swift
final class User: Model {
...
}
```
The compiler will inform you that some methods need to be implemented to conform.
### ID
The first required property is an identifier. This property will contain the identifier when the model is fetched from the database. If it is `nil`, it will be set when the model is saved.
```swift
final class User: Model {
var id: Node?
...
}
```
### Node Initializable
The next requirement is a way to create the model from the persisted data. Model uses `NodeInitializable` to achieve this.
```swift
final class User: Model {
init(node: Node, in context: Context) throws {
id = try node.extract("id")
name = try node.extract("name")
}
...
}
```
The keys `id` and `name` are what we expect the columns or fields in the database to be named. The `extract` call is marked with a `try` because it will throw an error if the value is not present or is the wrong type.
### Node Representable
Now that we have covered initializing the model, we need to show how to save it back into the database. Model uses `NodeRepresentable` to achieve this.
```swift
final class User: Model {
func makeNode(context: Context) throws -> Node {
return try Node(node: [
"id": id,
"name": name
])
}
...
}
```
When a `User` is saved, the `makeNode()` method will be called and the resulting `Node` will be saved to the database. The keys `id` and `name` are what we expect the columns or fields in the database to be named.
> In most of the cases you do not need to be concerned about `context` argument of the `makeNode(context:)` method. Its a part of the protocol that allows extensibility in more advanced or specific scenarios.
## Preparations
Some databases, like MySQL, need to be prepared for a new schema. In MySQL, this means creating a new table. Preparations are also equatable to migrations, as they can be used to alter schemas after they've already been created.
### Prepare
Let's assume we are using a SQL database. To prepare the database for our `User` class, we need to create a table. If you are using a database like Mongo, you can leave this method unimplemented.
```swift
final class User {
static func prepare(_ database: Database) throws {
try database.create("users") { users in
users.id()
users.string("name")
}
}
...
}
```
Here we create a table named `users` that has an identifier field and a string field with the key `name`. This matches both our `init(node: Node)` and `makeNode() -> Node` methods.
### Revert
An optional preparation reversion can be created. This will be run if `vapor run prepare --revert` is called.
```swift
final class User {
static func revert(_ database: Database) throws {
try database.delete("users")
}
...
}
```
Here we are deleting the table named `users`.
### Preparations as Migrations
If you want to add a field to your table after you've already created the initial schema, you can create a struct or class that conforms to `Preparation` like so:
```swift
struct AddFooToBar: Preparation {
static func prepare(_ database: Database) throws {
try database.modify("bars", closure: { bar in
bar.string("foo", length: 150, optional: false, unique: false, default: nil)
})
}
static func revert(_ database: Database) throws {
}
}
```
Then, in your Droplet setup, add this line: `drop.preparations.append(AddFooToBar.self)`
### Droplet
To run these prepations when the applications boots, you must add the Model to the `Droplet`.
```swift
let drop = Droplet()
drop.preparations.append(User.self)
```
> Note: Preparations must be appended before the Droplet is run.
## Full Model
This is what our final `User` model looks like:
```swift
import Vapor
import Fluent
final class User: Model {
var id: Node?
var name: String
init(name: String) {
self.name = name
}
init(node: Node, in context: Context) throws {
id = try node.extract("id")
name = try node.extract("name")
}
func makeNode(context: Context) throws -> Node {
return try Node(node: [
"id": id,
"name": name
])
}
static func prepare(_ database: Database) throws {
try database.create("users") { users in
users.id()
users.string("name")
}
}
static func revert(_ database: Database) throws {
try database.delete("users")
}
}
```
## Interacting
Now that `User` conforms to `Model`, it has a plethora of new methods like `find()`, `query()`, `makeJSON()` and more.
### Fetch
Models can be fetched by their database identifier.
```swift
let user = try User.find(42)
```
### Save
Newly created models can be saved to the database.
```swift
var user = User(name: "Vapor")
try user.save()
print(user.id) // prints the new id
```
### Delete
Persisted models with identifiers can be deleted.
```swift
try user.delete()
```
## Model vs. Entity
Model has a couple of extra conformances that a pure Fluent entity doesn't have.
```swift
public protocol Model: Entity, JSONRepresentable, StringInitializable, ResponseRepresentable {}
```
As can be seen in the protocol, Vapor models can automatically convert to `JSON`, `Response`, and even be used in type-safe routing.
## Options
Change the table/collection name
```swift
static var entity = "new_name"
```

View File

@ -1,120 +0,0 @@
---
currentMenu: fluent-query
---
# Query
The `Query` class is what powers every interaction with Fluent. Whether you're fetching a model with `.find()` or saving to the database, there is a `Query` involved somewhere.
## Querying Models
Every type that conforms to [Model](model.md) gets a static `.query()` method.
```swift
let query = try User.query()
```
This is how you can create a `Query<User>`.
### No Database
The `.query()` method is marked with `try` because it can throw an error if the Model has not had its static database property set.
```swift
User.database = drop.database
```
This property is set automatically when you pass the Model as a preparation.
## Filter
The most common types of queries involve filtering data.
```swift
let smithsQuery = try User.query().filter("last_name", "Smith")
```
Here is the short hand for adding an `equals` filter to the query. As you can see, queries can be chained together.
In additional to `equals`, there are many other types of `Filter.Comparison`.
```swift
let over21 = try User.query().filter("age", .greaterThanOrEquals, 21)
```
### Scope
Filters can also be run on sets.
```swift
let coolPets = try Pet.query().filter("type", .in, ["Dog", "Ferret"])
```
Here only Pets of type Dog _or_ Ferret are returned. The opposite works for `notIn`.
### Contains
Partially matching filters can also be applied.
```swift
let statesWithNew = try State.query().filter("name", contains: "New")
```
## Retrieving
There are two methods for running a query.
### All
All of the matching entities can be fetched. This returns an array of `[Model]`, in this case users.
```swift
let usersOver21 = try User.query().filter("age", .greaterThanOrEquals, 21).all()
```
### First
The first matching entity can be fetched. This returns an optional `Model?`, in this case a user.
```swift
let firstSmith = try User.query().filter("last_name", "Smith").first()
```
## Union
Other Models can be joined onto your query to assist in filtering. The results must still be either `[Model]` or `Model?` for whichever type created the query.
```swift
let usersWithCoolPets = try User.query()
.union(Pet.self)
.filter(Pet.self, "type", .in, ["Dog", "Ferret"])
```
Here the `User` collection is unioned to the `Pet` collection. Only `User`s who have a dog or a ferret will be returned.
### Keys
The `union` method assumes that the querying table has a foreign key identifier to the joining table.
The above example with users and pets assumes the following schema.
```
users
- id
pets
- id
- user_id
```
Custom foreign keys can be provided through overloads to `union`.
## Raw Queries
Since Fluent is focused on interacting with models, each Query requires a model type. If you want to do raw database queries that aren't based on a model, you should use the underlying Fluent Driver to do so.
```swift
if let mysql = drop.database?.driver as? MySQLDriver {
let version = try mysql.raw("SELECT @@version")
}
```

View File

@ -1,176 +0,0 @@
---
currentMenu: fluent-relation
---
# Relation
Relations allow foreign key based connections between database entities. This is common in SQL-based databases, but can also be used with NoSQL.
Fluent's relations are named as follows:
- Parent (BelongsTo)
- Children (HasMany, HasOne)
- Siblings (ManyToMany, BelongsToMany)
## Parent
The parent relation should be called on an entity that has a foreign key to another entity. For example, assume the following schema:
```
pets
- id
- owner_id
- name
- type
owner
- id
- name
```
Here each pet can have one owner. To access that owner from the pet, call `.parent()`.
```swift
let pet: Pet = ...
let owner = try pet.parent(pet.ownerId, Owner.self).get()
```
The parent method requires the foreign key for the parent as well as the type.
### Convenience
To make requesting a parent easier, a method can be added to the model.
```swift
extension Pet {
func owner() throws -> Parent<Owner> {
return try parent(ownerId)
}
}
```
Since we are extending `Pet`, we no longer need to use `pet.` before the `ownerId`. Furthermore, because we are providing the type information about `Owner` in the return type of the method, we no longer need to pass that as an argument.
The `Parent<T>` type is a queryable object, meaning you could `delete()` the parent, `filter()`, etc.
```swift
try pet.owner().delete()
```
To fetch the parent, you must call `get()`.
```swift
let owner = try pet.owner().get()
```
## Children
`Children` are the opposite side of the `Parent` relationship. Assuming the schema from the previous example, the pets could be retrieved from an owner like so:
```swift
let owner: Owner = ...
let pets = owner.children(Pet.self).all()
```
Here only the type of child is required.
### Convenience
Similarly to `Parent`, convenience methods can be added for children.
```swift
extension Owner {
func pets() throws -> Children<Pet> {
return try children()
}
}
```
Since the type information is clear from the return value, `Pet.self` does not need to be passed to `children()`.
The `Children<T>` is also a queryable object like `Parent<T>`. You can call `first()`, `all()`, `filter()`, etc.
```swift
let coolPets = try owner.pets().filter("type", .in, ["Dog", "Ferret"]).all()
```
## Siblings
`Siblings` work differently from `Children` or `Parent` since they require a `Pivot`.
For an example, let's say we want to allow our pets to have multiple toys. But we also want the toys to be shared by multiple pets. We need a pivot entity for this.
```
pets
- id
- type
- owner_id
toys
- id
- name
pets_toys
- id
- pet_id
- toy_id
```
Here you can see the pivot entity, `pets_toys`, or `Pivot<Pet, Toy>`.
### Convenience
Let's add the convenience methods to `Pet`.
```swift
extension Pet {
func toys() throws -> Siblings<Toy> {
return try siblings()
}
}
```
And the opposite for `Toy`.
```swift
extension Toy {
func pets() throws -> Siblings<Pet> {
return try siblings()
}
}
```
Now you are free to query pets and toys similarly to children.
```swift
let pet: Pet = ...
let toys = pet.toys().all()
```
To create a new many-to-many relationship you can do the following.
```swift
var toy: Toy = ... // Create a new toy
try toy.save() // Save the toy to the db
var pet: Pet = ... // Create a new pet
try pet.save() // Save the pet to the db
// Link them together in the db
var pivot = Pivot<Toy, Pet>(toy, pet) // Create the relationship
try pivot.save() // Save the relationship to the db
```
### Preparation
To prepare for a relationship with a `Pivot`, simply add the pivot to the `Droplet`'s preparations.
```swift
let drop = Droplet()
drop.preparations += [
Toy.self,
Pet.self,
Pivot<Toy, Pet>.self
]
```

View File

@ -1,136 +0,0 @@
---
currentMenu: getting-started-hello-world
---
# Hello, World
This section assumes you have installed Swift 3 and the Vapor Toolbox and have verified they are working.
> Note: If you don't want to use the Toolbox, follow the [manual guide](manual.md).
## New Project
Let's start by creating a new project called Hello, World
```sh
vapor new Hello
```
Vapor's folder structure will probably look familiar to you if you have worked with other web frameworks.
```
Hello
├── Sources
│ └── App
│ └── Controllers
│ └── Middleware
│ └── Models
│ └── main.swift
├── Public
├── Resources
│ └── Views
└── Package.swift
```
For our Hello, World project, we will be focusing on the `main.swift` file.
```
Hello
└── Sources
└── App
└── main.swift
```
Note: The `vapor new` command creates a new project with examples and comments about how to use the framework. You can delete these if you want.
## Droplet
Look for the following line in the `main.swift` file.
```swift
let drop = Droplet()
```
This is where the one and only `Droplet `for this example will be created. The `Droplet` class has a plethora of useful functions on it, and is used extensively.
## Routing
Right after the creation of `drop`, add the following code snippet.
```swift
drop.get("hello") { request in
return "Hello, world!"
}
```
This creates a new route on the `Droplet` that will match all `GET` requests to `/hello`.
All route closures are passed an instance of [Request](../http/request.md) that contains information such as the URI requested and data sent.
This route simply returns a string, but anything that is [ResponseRepresentable](../http/response-representable.md) can be returned. Learn more in the [Routing](../routing/basic.md) section of the guide.
Note: Xcode autocomplete may add extraneous type information to your closure's input arguments. This can be deleted to keep the code clean. If you'd like to keep the type information add `import HTTP` to the top of the file.
## Running
At the bottom of the main file, make sure to serve your `Droplet`.
```swift
drop.run()
```
Save the file, and switch back to the terminal.
## Compiling
A big part of what makes Vapor so great is Swift's state of the art compiler. Let's fire it up. Make sure you are in the root directory of the project and run the following command.
```swift
vapor build
```
Note: `vapor build` runs `swift build` in the background.
The Swift Package Manager will first start by downloading the appropriate dependencies from git. It will then compile and link these dependencies together.
When the process has completed, you will see `Building Project [Done]`
Note: If you see a message like `unable to execute command: Killed`, you need to increase your swap space. This can happen if you are running on a machine with limited memory.
## Run
Boot up the server by running the following command.
```swift
vapor run serve
```
You should see a message `Server starting...`. You can now visit `http://localhost:8080/hello` in your browser.
Note: Certain port numbers require super user access to bind. Simply run `sudo vapor run` to allow access. If you decide to run on a port besides `80`, make sure to direct your browser accordingly.
## Note for sudo usage
On some Linux based systems, you might get an error while using sudo. In that case, if you need to run the server as root, at first switch the user using this command:
```
sudo -i
```
Then either add the previously installed path of Swift to the root users $PATH variable.
```
PATH=$PATH:/your_path_to_swift
# Example command can be like this
# PATH=$PATH:/swift-3.0/usr/bin
# In this case /swift-3.0/usr/bin is the location of my swift installation.
```
## Hello, World
You should see the following output in your browser window.
```
Hello, world!
```

View File

@ -1,33 +0,0 @@
---
currentMenu: getting-started-install-swift-3-macos
---
# Install Swift 3: macOS
To use Swift 3 on macOS, you just need to have Xcode 8 installed.
## Install Xcode
Install [Xcode 8](https://itunes.apple.com/us/app/xcode/id497799835?mt=12) from the Mac App Store.
[![Xcode 8](https://cloud.githubusercontent.com/assets/1342803/18537674/2ddd8e9c-7ad5-11e6-9bc2-7155d57d20ec.png)](https://itunes.apple.com/us/app/xcode/id497799835?mt=12)
### Open Xcode
After Xcode 8 has been downloaded, you must open it to finish the installation. This may take a while.
## Check
Double check the installation was successful by running:
```sh
curl -sL check.vapor.sh | bash
```
## Toolbox
You can now move on to [Install Toolbox](install-toolbox.md).
## Swift.org
Check out [Swift.org](https://swift.org)'s extensive guides if you need more detailed instructions for installing Swift 3.0.

View File

@ -1,98 +0,0 @@
---
currentMenu: getting-started-install-swift-3-ubuntu
---
# Install Swift 3: Ubuntu
Installing Swift 3 on Ubuntu only takes a couple of minutes.
## Quick
Don't want to type? Run the following script to quickly install Swift 3.0.
```sh
curl -sL swift.vapor.sh/ubuntu | bash
```
> Note: The install script adds Swift to your `~/.bashrc` profile automatically.
## Manual
### Dependencies
Depending on your version of Ubuntu, you may need some additional tools for the compiler. We'll err on the safe side and install everything you should need
```sh
sudo apt-get update
sudo apt-get install clang libicu-dev binutils git libpython2.7-dev libcurl3
```
### Download
Download the Swift 3 toolchain for your Ubuntu version.
```sh
# Ubuntu 14.04
wget https://swift.org/builds/swift-3.0-release/ubuntu1404/swift-3.0-RELEASE/swift-3.0-RELEASE-ubuntu14.04.tar.gz
# Ubuntu 15.10
wget https://swift.org/builds/swift-3.0-release/ubuntu1510/swift-3.0-RELEASE/swift-3.0-RELEASE-ubuntu15.10.tar.gz
```
### Decompress
After Swift 3 has downloaded, decompress it.
```sh
# Ubuntu 14.04
tar zxf swift-3.0-RELEASE-ubuntu14.04.tar.gz
# Ubuntu 15.10
tar zxf swift-3.0-RELEASE-ubuntu15.10.tar.gz
```
### Install
Move Swift 3.0 to a safe, permanent place on your computer. We'll use `/swift-3.0`, but feel free to choose wherever you like.
```sh
# Ubuntu 14.04
mv swift-3.0-RELEASE-ubuntu14.04 /swift-3.0
# Ubuntu 15.10
mv swift-3.0-RELEASE-ubuntu15.10 /swift-3.0
```
> Note: You may need to use `sudo`.
### Export
Edit your bash profile using your text editor of choice.
```sh
vim ~/.bashrc
```
Add the following line:
```sh
export PATH=/swift-3.0/usr/bin:"${PATH}"
```
> Note: If you moved Swift 3.0 to a folder other than `/swift-3.0`, your path will be different.
## Check
Double check the installation was successful by running:
```sh
curl -sL check.vapor.sh | bash
```
## Toolbox
You can now move on to [Install Toolbox](install-toolbox.md)
## Swift.org
Check out [Swift.org](https://swift.org)'s extensive guides if you need more detailed instructions for installing Swift 3.0.

View File

@ -1,57 +0,0 @@
---
currentMenu: getting-started-install-toolbox
---
# Install Toolbox
Vapor's command line interface provides shortcuts and assistance for common tasks.
![Vapor Toolbox](https://cloud.githubusercontent.com/assets/1342803/17454691/97e549e2-5b6d-11e6-979a-f0cd6b6f1b0a.png)
> If you do not want to install the Toolbox, checkout the [Manual](manual.md) quickstart.
### Install
Run the following script to install the [Toolbox](https://github.com/vapor/toolbox).
```sh
curl -sL toolbox.vapor.sh | bash
```
> Note: You must have the correct version of Swift 3 installed.
### Verify
Make sure the Toolbox installed successfully by running the help query. You should see a print out of the available commands. You can run the `--help` option on any Toolbox command.
```sh
vapor --help
```
## Create A Project
Now that you have installed the Toolbox, you can create your first Vapor project following the [Hello, World guide](hello-world.md).
### Updating
The toolbox can update itself. This may be useful if you experience any issues in the future.
```sh
vapor self update
```
### Templates
The toolbox can create a project from the Vapor basic-template or any other git repo.
```sh
vapor new <name> [--template=<repo-url-or-github-path>]
```
The toolbox will build an absolute URL based on what you pass as the template option. If you do not specify a template option, the project will be built from the Vapor basic-template.
```sh
Default(no template option specified) => https://github.com/vapor/basic-template
http(s)://example.com/repo-path => http(s)://example.com/repo-path
user/repo => https://github.com/user/repo
light => https://github.com/vapor/light-template
```

View File

@ -1,99 +0,0 @@
---
currentMenu: getting-started-manual
---
# Manual Quickstart
Learn how to create a Vapor project _without_ the Toolbox using just Swift 3 and the Swift Package Manager.
> If you'd prefer to use the Toolbox, learn how to install it [here](install-toolbox.md).
This document assumes that you have Swift 3 installed.
> Note: If you've installed the Toolbox, follow the toolbox guide [here](hello-world.md).
## Check
To check that your environment is compatible, run the following script:
```bash
curl -sL check.vapor.sh | bash
```
## Make new project using SwiftPM
Open your terminal
> For our example, we'll be using the Desktop folder.
```bash
cd ~/Desktop
mkdir Hello
cd Hello
swift package init --type executable
```
Your folder should look like this:
```
├── Package.swift
├── Sources
│   └── main.swift
└── Tests
```
## Edit `Package.swift`
Open your `Package.swift` file:
```bash
open Package.swift
```
And add Vapor as a dependency. Here's how your file will look.
#### Package.swift
```swift
import PackageDescription
let package = Package(
name: "Hello",
dependencies: [
.Package(url: "https://github.com/vapor/vapor.git", majorVersion: 1, minor: 1)
]
)
```
> We try to keep this document up to date, however, you can view latest releases [here](https://github.com/vapor/vapor/releases)
## Edit `main.swift`
A simple hello world:
```
import Vapor
let drop = Droplet()
drop.get("/hello") { _ in
return "Hello Vapor"
}
drop.run()
```
## Build and Run
The first `build` command can take a while to fetch dependencies.
```
swift build
.build/debug/Hello
```
> If different, replace `Hello` above with the name of your executable.
## View
Go to your favorite browser and visit `http://localhost:8080/hello`

View File

@ -1,45 +0,0 @@
---
currentMenu: getting-started-xcode
---
# Xcode
The first thing you'll probably notice about Vapor and SwiftPM projects in general is that we don't include an Xcode project. In fact, when SwiftPM generates packages, the `.xcodeproj` file is gitignored by default.
This means we don't have to worry about pbxproj conflicts, and it's easy for different platforms to utilize their own editors.
## Generate Project
### Vapor Toolbox
To generate a new Xcode project for a project, use:
```bash
vapor xcode
```
> If you'd like to automatically open the Xcode project, use `vapor xcode -y`
### Manual
To generate a new Xcode project manually.
```bash
swift package generate-xcodeproj
```
Open the project and continue normally.
## Flags
For many packages with underlying c-dependencies, users will need to pass linker flags during **build** AND **project generation**. Make sure to consult the guides associated with those dependencies. For example:
```
vapor xcode --mysql
```
or
```
swift package generate-xcodeproj -Xswiftc -I/usr/local/include/mysql -Xlinker -L/usr/local/lib
```

View File

@ -1,45 +0,0 @@
---
currentMenu: guide-commands
---
# Commands
Custom console commands on Vapor are a breeze.
## Example
To make a custom console command we must first create a new `.swift` file, import `Vapor` and `Console`, and implement the `Command` protocol.
```swift
import Vapor
import Console
final class MyCustomCommand: Command {
public let id = "command"
public let help = ["This command does things, like foo, and bar."]
public let console: ConsoleProtocol
public init(console: ConsoleProtocol) {
self.console = console
}
public func run(arguments: [String]) throws {
console.print("running custom command...")
}
}
```
- The **id** property is the string you will type in the console to access the command. `.build/debug/App command` will run the Custom Command.
- The **help** property is the help message that will give your custom command's users some idea of how to access it.
- The **console** property is the object passed to your custom command that adheres to the console protocol, allowing manipulation of the console.
- The **run** method is where you put the logic relating to your command.
After we work our magic in the Custom Command file, we switch over to our `main.swift` file and add the custom command to the droplet like so.
```swift
drop.commands.append(MyCustomCommand(console: drop.console))
```
This allows Vapor access to our custom command and lets it know to display it in the `--help` section of the program.
After compiling the application we can run our custom command like so.
```
.build/debug/App command
```

View File

@ -1,185 +0,0 @@
---
currentMenu: guide-config
---
# Config
An application's configuration settings. Cloud applications generally require complex configurations that can adjust based on their environment. Vapor intends to provide a flexible configuration interaction that can be customized for a given user.
## QuickStart
For Vapor applications, configuration files are expected to be nested under a top level folder named `Config`. Here's an example of a basic config featuring a single `servers` configuration.
```bash
./
├── Config/
│ ├── servers.json
```
And an example of how this might look:
```JSON
{
"http": {
"host": "0.0.0.0",
"port": 8080
}
}
```
What that's saying, is that our application should start a single server named 'http' serving port `8080` on host `0.0.0.0`. This represents the following url: `http://localhost:8080`.
### Custom Keys
Let's add a custom key to the `servers.json` file:
```JSON
{
"http": {
"host": "0.0.0.0",
"port": 8080,
"custom-key": "custom value"
}
}
```
This can be accessed from your application's config using the following.
```swift
let customValue = drop.config["servers", "http", "custom-key"]?.string ?? "default"
```
That's it, feel free to add and utilize keys as necessary to make your application configuration easier.
## Config Syntax
You can access your config directory with the following syntax. `app.config[<#file-name#>, <#path#>, <#to#>, <#file#>]`. For example, let's hypothesize that in addition to the `servers.json` file we mentioned earlier, there is also a `keys.json` that looks like this:
```JSON
{
"test-names": [
"joe",
"jane",
"sara"
],
"mongo": {
"url" : "www.customMongoUrl.com"
}
}
```
We can access this file by making sure the first argument in our subscript is keys. To get the first name in our list:
```swift
let name = drop.config["keys", "test-names", 0]?.string ?? "default"
```
Or our mongo url:
```swift
let mongoUrl = drop.config["keys", "mongo", "url"]?.string ?? "default"
```
## Advanced Configurations
Having the default `servers.json` is great, but what about more complex scenarios. For example, what if we want a different host in production and in development? These complex scenarios can be achieved by adding additional folders to our `Config/` directory. Here's an example of a folder structure that's setup for production and development environments.
```bash
WorkingDirectory/
├── Config/
│ ├── servers.json
│ ├── production/
│ │ └── servers.json
│ ├── development/
│ │ └── servers.json
│ └── secrets/
│ └── servers.json
```
> You can specify the environment through the command line by using --env=. Custom environments are also available, a few are provided by default: production, development, and testing.
```bash
vapor run --env=production
```
### PRIORITY
Config files will be accessed in the following priority.
1. CLI (see below)
2. Config/secrets/
3. Config/name-of-environment/
4. Config/
What this means is that if a user calls `app.config["servers", "host"]`, the key will be searched in the CLI first, then the `secrets/` directory, then the top level default configs.
> `secrets/` directory should very likely be added to the gitignore.
### EXAMPLE
Let's start with the following JSON files.
#### `servers.json`
```JSON
{
"http": {
"host": "0.0.0.0",
"port": 9000
}
}
```
#### `production/servers.json`
```JSON
{
"http": {
"host": "127.0.0.1",
"port": "$PORT"
}
}
```
> The `"$NAME"` syntax is available for all values to access environment variables.
Please notice that `servers.json`, and `production/servers.json` both declare the same keys: `host`, and `port`. In our application, we'll call:
```swift
// will load 0.0.0.0 or 127.0.0.1 based on above config
let host = drop.config["servers", "http", "host"]?.string ?? "0.0.0.0"
// will load 9000, or environment variable port.
let port = drop.config["servers", "http", "port"]?.int ?? 9000
```
## COMMAND LINE
In addition to json files nested within the `Config/` directory, we can also use the command line to pass arguments into our config. By default, these values will be set as the "cli" file, but more complex options are also available.
#### 1. `--KEY=VALUE`
Arguments set through the command line can be accessed through config's cli file. For example, the following CLI command:
```bash
--mongo-password=$MONGO_PASSWORD
```
would be accessible within your application by using the following:
```swift
let mongoPassword = drop.config["cli", "mongo-password"]?.string
```
#### 2. `--CONFIG:FILE-NAME.KEY=CUSTOM-VALUE`
If you want command line arguments set to a file besides "cli", you can use this more advanced specification. For example, the following CLI command:
```bash
--config:keys.analytics=124ZH61F
```
would be accessible within your application by using the following:
```swift
let analyticsKey = drop.config["keys", "analytics"]?.string
```

View File

@ -1,139 +0,0 @@
---
currentMenu: guide-controllers
---
# Controllers
Controllers help you organize related functionality into a single place. They can also be used to create RESTful resources.
## Basic
A basic controller looks like the following:
```swift
final class HelloController {
func sayHello(_ req: Request) throws -> ResponseRepresentable {
guard let name = req.data["name"] else {
throw Abort.badRequest
}
return "Hello, \(name)"
}
}
```
Simple controllers don't need to conform to any protocols. You are free to design them however you see fit.
### Registering
The only required structure is the signature of each method in the controller. In order to register this method into the router, it must have a signature like `(Request) throws -> ResponseRepresentable`. `Request` and `ResponseRepresentable` are made available by importing the `HTTP` module.
```swift
let hc = HelloController()
drop.get("hello", handler: hc.sayHello)
```
Since the signature of our `sayHello` method matches the signature of the closure for the `drop.get` method, we can pass it directly.
### Type Safe
You can also use controller methods with type-safe routing.
```swift
final class HelloController {
...
func sayHelloAlternate(_ req: Request, _ name: String) -> ResponseRepresentable {
return "Hello, \(name)"
}
}
```
We add a new method called `sayHelloAlternate` to the `HelloController` that accepts a second parameter `name: String`.
```swift
let hc = HelloController()
drop.get("hello", String.self, handler: hc.sayHelloAlternate)
```
Since this type-safe `drop.get` accepts a signature `(Request, String) throws -> ResponseRepresentable`, our method can now be used as the closure for this route.
## Resources
Controllers that conform to `ResourceRepresentable` can be easily registered into a router as a RESTful resource. Let's look at an example of a `UserController`.
```swift
final class UserController {
func index(_ request: Request) throws -> ResponseRepresentable {
return try User.all().makeNode().converted(to: JSON.self)
}
func show(_ request: Request, _ user: User) -> ResponseRepresentable {
return user
}
}
```
Here is a typical user controller with an `index` and `show` route. Indexing returns a JSON list of all users and showing returns a JSON representation of a single user.
We _could_ register the controller like so:
```swift
let users = UserController()
drop.get("users", handler: users.index)
drop.get("users", User.self, handler: users.show)
```
But `ResourceRepresentable` makes this standard RESTful structure easy.
```swift
extension UserController: ResourceRepresentable {
func makeResource() -> Resource<User> {
return Resource(
index: index,
show: show
)
}
}
```
Conforming `UserController` to `ResourceRepresentable` requires that the signatures of the `index` and `show` methods match what the `Resource<User>` is expecting.
Here is a peek into the `Resource` class.
```swift
final class Resource<Model: StringInitializable> {
typealias Multiple = (Request) throws -> ResponseRepresentable
typealias Item = (Request, Model) throws -> ResponseRepresentable
var index: Multiple?
var store: Multiple?
var show: Item?
var replace: Item?
var modify: Item?
var destroy: Item?
var clear: Multiple?
var aboutItem: Item?
var aboutMultiple: Multiple?
...
}
```
Now that `UserController` conforms to `ResourceRepresentable`, registering the routes is easy.
```swift
let users = UserController()
drop.resource("users", users)
```
`drop.resource` will take care of registering only the routes that have been supplied by the call to `makeResource()`. In this case, only the `index` and `show` routes will be supplied.
> Note: `drop.resource` also adds useful defaults for OPTIONS requests. These can be overriden.
## Folder
Controllers can go anywhere in your application, but they are most often stored in the `Controllers/` directory.
### Modules
If you are building a large application, you may want to create your controllers in a separate module. This will allow you to perform unit tests on your controllers. For more information on creating modules, visit the documentation for the [Swift Package Manager](https://swift.org/package-manager/).

View File

@ -1,109 +0,0 @@
---
currentMenu: guide-droplet
---
# Droplet
The `Droplet` is a service container that gives you access to many of Vapor's facilities. It is responsible for registering routes, starting the server, appending middleware, and more.
## Initialization
As you have probably already seen, the only thing required to create an instance of `Droplet` is to import Vapor.
```swift
import Vapor
let drop = Droplet()
// your magic here
drop.run()
```
Creation of the `Droplet` normally happens in the `main.swift` file.
## Environment
The `environment` property contains the current environment your application is running in. Usually development, testing, or production.
```swift
if drop.environment == .production {
...
}
```
The environment affects [Config](config.md) and [Logging](log.md). The environment is `development` by default. To change it, pass the `--env=` flag as an argument.
```sh
vapor run serve --env=production
```
If you are in Xcode, you can pass arguments through the scheme editor.
> Note: Debug logs can reduce the number of requests your application can handle per second. Enabling the production environment can improve performance.
## Working Directory
The `workDir` property contains a path to the current working directory of the application relative to where it was started. By default, this property assumes you started the Droplet from its root directory.
```swift
drop.workDir // "/var/www/my-project/"
```
You can override the working directory through the `Droplet`'s initializer, or by passing the `--workdir` argument.
```sh
vapor run serve --workdir="/var/www/my-project"
```
## Modifying Properties
The `Droplet`'s properties can be changed programmatically or through configuration.
### Programmatic
Properties on the `Droplet` can be changed after it is initialized.
```swift
let drop = Droplet()
drop.server = MyServerType.self
```
Here the type of server the `Droplet` uses is changed to a custom type. When the `Droplet` is run, this custom server type will be booted instead of the default server.
### Configurable
If you want to modify a property of the `Droplet` only in certain cases, you can use `addConfigurable`. Say for example you want to email error logs to yourself in production, but you don't want to spam your inbox while developing.
```swift
let drop = Droplet()
drop.addConfigurable(log: MyEmailLogger.self, name: "email")
```
The `Droplet` will continue to use the default logger until you modify the `Config/droplet.json` file to point to your email logger. If this is done in `Config/production/droplet.json`, then your logger will only be used in production.
```json
{
"log": "email"
}
```
## Initialization
The `Droplet` init method is fairly simple since most properties are variable and can be changed after initialization.
Most plugins for Vapor come with a [Provider](provider.md), these take care of configuration details for you.
```swift
Droplet(
arguments: [String]?,
workDir workDirProvided: String?,
config configProvided: Config?,
localization localizationProvided: Localization?,
)
```
> Note: Remember that the Droplet's properties are initialized with usable defaults. This means that if you change a property, you must be sure to change it _before_ other parts of your code use it. Otherwise, you may end up with confusing results as the defaults are used sometimes, and your overrides are used other times.

View File

@ -1,88 +0,0 @@
---
currentMenu: guide-folder-structure
---
# Folder Structure
The first step to creating an awesome application is knowing where things are. If you created your project using the [Toolbox](../getting-started/toolbox.md) or from a template, you will already have the folder structure created.
If you are making a Vapor application from scratch, this will show you exactly how to set it up.
## Minimum Folder Structure
We recommend putting all of your Swift code inside of the `App/` folder. This will allow you to create subfolders in `App/` to organize your models and resources.
This works best with the Swift package manager's restrictions on how packages should be structured.
```
.
├── App
│ └── main.swift
├── Public
└── Package.swift
```
The `Public` folder is where all publicly accessible files should go. This folder will be automatically checked every time a URL is requested that is not found in your routes.
> Note: The `FileMiddleware` is responsible for accessing files from the `Public` folder.
## Models
The `Models` folder is a recommendation of where you can put your database and other models, following the MVC pattern.
```
.
├── App
. └── Models
. └── User.swift
```
## Controllers
The `Controllers` folder is a recommendation of where you can put your route controllers, following the MVC pattern.
```
.
├── App
. └── Controllers
. └── UserController.swift
```
## Views
The `Views` folder in `Resources` is where Vapor will look when you render views.
```
.
├── App
└── Resources
└── Views
└── user.html
```
The following code would load the `user.html` file.
```swift
drop.view.make("user.html")
```
## Config
Vapor has a sophisticated configuration system that involves a hierarchy of configuration importance.
```
.
├── App
└── Config
└── app.json // default app.json
└── development
└── app.json // overrides app.json when in development environment
└── production
└── app.json // overrides app.json when in production environment
└── secrets
└── app.json // overrides app.json in all environments, ignored by git
```
`.json` files are structured in the `Config` folder as shown above. The configuration will be applied dependant on where the `.json` file exists in the hierarchy. Learn more in [Config](config.md).
Learn about changing environments (the `--env=` flag) in the [Droplet](droplet.md) section.

View File

@ -1,36 +0,0 @@
---
currentMenu: guide-hash
---
# Hash
Vapor makes hashing strings easy.
## Example
To hash a string, use the `hash` class on `Droplet`.
```swift
let hashed = drop.hash.make("vapor")
```
## SHA2Hasher
By default, Vapor uses a SHA2Hasher with 256 bits. You can change this by giving the `Droplet` a different hasher.
```swift
let sha512 = SHA2Hasher(variant: .sha512)
let drop = Droplet(hash: sha512)
```
### Protocol
You can also create your own hasher. You just need to conform to the `Hash` protocol.
```swift
public protocol Hash: class {
var key: String { get set }
func make(_ string: String) -> String
}
```

View File

@ -1,53 +0,0 @@
---
currentMenu: guide-json
---
# JSON
JSON is an integral part of Vapor. It powers Vapor's [Config](config.md) and is incredibly easy to use in both requests and responses.
## Request
JSON is automatically available in `request.data` alongside form-urlencoded data and query data. This allows you to focus on making a great API, not worrying about what content types data will be sent in.
```swift
drop.get("hello") { request in
guard let name = request.data["name"]?.string else {
throw Abort.badRequest
}
return "Hello, \(name)!"
}
```
This will return a greeting for any HTTP method or content type that the `name` is sent as, including JSON.
### JSON Only
To specifically target JSON, use the `request.json` property.
```swift
drop.post("json") { request in
guard let name = request.json?["name"]?.string else {
throw Abort.badRequest
}
return "Hello, \(name)!"
}
```
The above snippet will only work if the request is sent with JSON data.
## Response
To respond with JSON, simply wrap your data structure with `JSON(node: )`
```swift
drop.get("version") { request in
return try JSON(node: [
"version": "1.0"
])
}
```
## Middleware
The `JSONMiddleware` is included in the `Droplet`'s middleware by default. You can remove it if you don't want JSON to be parsed.

View File

@ -1,185 +0,0 @@
---
currentMenu: guide-leaf
---
# Leaf
Welcome to Leaf. Leaf's goal is to be a simple templating language that can make generating views easier. There's a lot of great templating languages, use what's best for you, maybe that's Leaf! The goals of Leaf are as follows:
- Small set of strictly enforced rules
- Consistency
- Parser first mentality
- Extensibility
## Syntax
### Structure
Leaf Tags are made up of 4 Elements:
- Token: `#` is the Token
- Name: A `string` that identifies the tag
- Parameter List: `()` May accept 0 or more arguments
- Body (optional): `{}` Must be separated from the Parameter List by a space
There can be many different usages of these 4 elements depending on the Tag's implementation. Let's look at a few examples of how Leaf's built-in Tags might be used:
- `#()`
- `#(variable)`
- `#import("template")`
- `#export("link") { <a href="#()"></a> }`
- `#index(friends, "0")`
- `#loop(friends, "friend") { <li>#(friend.name)</li> }`
- `#raw() { <a href="#raw">Anything goes!@#$%^&*</a> }`
### Using the `#` token in HTML
The `#` token cannot be escaped. Use the `#()` or `#raw() {}` Tag to output a `#` in a Leaf Template. `#()` => `#`
### Raw HTML
All Leaf output is escaped by default. Use the `#raw() {}` Tag for unescaped output.
`#raw() { <a href="#link">Link</a> }` => `<a href="#link">Link</a>`
> IMPORTANT! Make sure you are not using the `#raw() {}` Tag with user input.
### Chaining
The double token: `##` indicates a chain. It can be applied to any standard Tag. If the previous Tag fails, the chained Tag will be given an opportunity to run.
```
#if(hasFriends) ##embed("getFriends")
```
### Leaf's built-in Tags
#### Token: `#()`
```
#() #()hashtags #()FTW => # #Hashtags #FTW
```
#### Raw: `#raw() {}`
```
#raw() {
Do whatever w/ #'s here, this code won't be rendered as leaf document and is not escaped.
It's a great place for things like Javascript or large HTML sections.
}
```
#### Equal: `#equal(lhs, rhs) {}`
```
#equal(leaf, leaf) { Leaf == Leaf } => Leaf == Leaf
#equal(leaf, mustache) { Leaf == Mustache } =>
```
#### Variable: `#(variable)`
```
Hello, #(name)!
```
#### Loop: `#loop(object, "index")`
```
#loop(friends, "friend") {
Hello, #(friend.name)!
}
```
#### Index: `#index(object, _ index: Int|String)`
```
Hello, #index(friends, 0)!
Hello, #index(friends, "best")!
```
#### If - Else: `#if(bool) ##else() { this }`
```
#if(entering) {
Hello, there!
} ##if(leaving) {
Goodbye!
} ##else() {
I've been here the whole time.
}
```
#### Import: `#import("template")`
#### Export: `#export("template") { Leaf/HTML }`
#### Extend: `#extend("template")`
#### Embed: `#embed("template")`
> When using these Layout Tags, omit the template file's .leaf extension.
```
/// base.leaf
<!DOCTYPE html>
#import("html")
/// html.leaf
#extend("base")
#export("html") { <html>#embed("body")</html> }
/// body.leaf
<body></body>
```
Leaf renders `html.leaf` as:
```
<!DOCTYPE html>
<html><body></body></html>
```
### Custom Tags
Look at the existing tags for advanced scenarios, let's look at a basic example by creating `Index` together. This tag will take two arguments, an array, and an index to access.
```swift
class Index: BasicTag {
let name = "index"
func run(arguments: [Argument]) throws -> Node? {
guard
arguments.count == 2,
let array = arguments[0].value?.nodeArray,
let index = arguments[1].value?.int,
index < array.count
else { return nil }
return array[index]
}
}
```
We can now register this Tag in our `main.swift` file with:
```swift
if let leaf = drop.view as? LeafRenderer {
leaf.stem.register(Index())
}
```
And use it just like we did [above](#index).
> Note: Use of non-alphanumeric characters in Tag Names is **strongly discouraged** and may be disallowed in future versions of Leaf.
## Syntax Highlighting
### Atom
[language-leaf](https://atom.io/packages/language-leaf) by ButkiewiczP
### Xcode
It is not currently possible to implement Leaf Syntax Highlighting in Xcode, however, using Xcode's HTML Syntax Coloring can help a bit. Select one or more Leaf files and then choose Editor > Syntax Coloring > HTML. Your selected Leaf files will now use Xcode's HTML Syntax Coloring. Unfortunately the usefulness of this is limited because this association will be removed when `vapor xcode` is run.
There appears to be a way to [make Xcode file associations persist](http://stackoverflow.com/questions/9050035/how-to-make-xcode-recognize-a-custom-file-extension-as-objective-c-for-syntax-hi) but that requires a bit more kung-fu.
### VS Code
[html-leaf](https://marketplace.visualstudio.com/items?itemName=Francisco.html-leaf) by FranciscoAmado
### CLion & AppCode
Some preliminary work has been done to implement a Leaf Plugin for CLion & AppCode but lack of skill and interest in Java has slowed progress! If you have IntelliJ SDK experience and want to help with this, message Tom Holland on [Vapor Slack](http://vapor.team)

View File

@ -1,251 +0,0 @@
---
currentMenu: guide-middleware
---
# Middleware
Middleware is an essential part of any modern web framework. It allows you to modify requests and responses as they pass between the client and your server.
You can imagine middleware as a chain of logic connecting your server to the client requesting your web app.
## Basic
As an example, let's create a middleware that will add the version of our API to each response. The middleware would look something like this:
```swift
final class VersionMiddleware: Middleware {
func respond(to request: Request, chainingTo next: Responder) throws -> Response {
let response = try next.respond(to: request)
response.headers["Version"] = "API v1.0"
return response
}
}
```
We then supply this middleware to our `Droplet`.
```swift
let drop = Droplet()
drop.middleware.append(VersionMiddleware())
```
You can imagine our `VersionMiddleware` sitting in the middle of a chain that connects the client and our server. Every request and response that hits our server must go through this chain of middleware.
![Middleware](https://cloud.githubusercontent.com/assets/1342803/17382676/0b51d6d6-59a0-11e6-9cbb-7585b9ab9803.png)
## Breakdown
Let's break down the middleware line by line.
```swift
let response = try next.respond(to: request)
```
Since the `VersionMiddleware` in this example is not interested in modifying the request, we immediately ask the next middleware in the chain to respond to the request. This goes all the way down the chain to the `Droplet` and comes back with the response that should be sent to the client.
```swift
response.headers["Version"] = "API v1.0"
```
We then _modify_ the response to contain a Version header.
```swift
return response
```
The response is returned and will chain back up any remaining middleware and back to the client.
## Request
The middleware can also modify or interact with the request.
```swift
func respond(to request: Request, chainingTo next: Responder) throws -> Response {
guard request.cookies["token"] == "secret" else {
throw Abort.badRequest
}
return try next.respond(to: request)
}
```
This middleware will require that the request has a cookie named `token` that equals `secret` or else the request will be aborted.
## Errors
Middleware is the perfect place to catch errors thrown from anywhere in your application. When you let the middleware catch errors, you can remove a lot of duplicated logic from your route closures. Take a look at the following example:
```swift
enum FooError: Error {
case fooServiceUnavailable
}
```
Say there is a custom error that either you defined or one of the APIs you are using `throws`. This error must be caught when thrown, or else it will end up as a server error which may be unexpected to a user. The most obvious solution is to catch the error in the route closure.
```swift
app.get("foo") { request in
let foo: Foo
do {
foo = try getFooFromService()
} catch {
throw Abort.badRequest
}
// continue with Foo object
}
```
This solution works, but it would get repetitive if repeated throughout multiple routes. It can also easily lead to code duplication. Luckily, this error could be caught in a middleware instead.
```swift
final class FooErrorMiddleware: Middleware {
func respond(to request: Request, chainingTo next: Responder) throws -> Response {
do {
return try next.respond(to: request)
} catch FooError.fooServiceUnavailable {
throw Abort.custom(
status: .badRequest,
message: "Sorry, we were unable to query the Foo service."
)
}
}
}
```
We just need to append this middleware to the `Droplet`.
```swift
drop.middleware.append(FooErrorMiddleware())
```
Now our route closures look a lot better and we don't have to worry about code duplication.
```swift
app.get("foo") { request in
let foo = try getFooFromService()
// continue with Foo object
}
```
Interestingly, this is how `Abort` itself is implemented in Vapor. `AbortMiddleware` catches any `Abort` errors and returns a JSON response. Should you want to customize how `Abort` errors appear, you can remove this middleware and add your own.
## Configuration
Appending middleware to the `drop.middleware` array is the simplest way to add middleware--it will be used every time the application starts.
You can also use the [configuration](config.md) files to enabled or disable middleware for more control. This is especially useful if you have middleware that should, for example, run only in production.
Appending configurable middleware looks like the following:
```swift
let drop = Droplet()
drop.addConfigurable(middleware: myMiddleware, name: "my-middleware")
```
Then, in the `Config/droplet.json` file, add `my-middleware` to the appropriate `middleware` array.
```json
{
...
"middleware": {
"server": [
...
"my-middleware",
...
],
"client": [
...
]
},
...
}
```
If the name of the added middleware appears in the `server` array for the loaded configuration, it will be added to the server's middleware when the application boots.
Likewise, if the middleware appears in the `client` array for the loaded configuration, it will be added to the client's middleware.
One middleware can be appended to both the Client and the Server, and can be added multiple times. The ordering of middleware is respected.
## Extensions (Advanced)
Middleware pairs great with request/response extensions and storage.
```swift
final class PokemonMiddleware: Middleware {
let drop: Droplet
init(drop: Droplet) {
self.drop = drop
}
func respond(to request: Request, chainingTo next: Responder) throws -> Response {
let response = try next.respond(to: request)
if let pokemon = response.pokemon {
if request.accept.prefers("html") {
response.view = try drop.view("pokemon.mustache", context: pokemon)
} else {
response.json = try pokemon.makeJSON()
}
}
return response
}
}
```
And the extension to `Response`.
```swift
extension Response {
var pokemon: Pokemon? {
get { return storage["pokemon"] as? Pokemon }
set { storage["pokemon"] = newValue }
}
}
```
In this example, we added a new property to response capable of holding a Pokémon object. If the middleware finds a response with one of these Pokémon objects, it will dynamically check whether the client prefers HTML. If the client is a browser like Safari and prefers HTML, it will return a Mustache view. If the client does not prefer HTML, it will return JSON.
Your closures can now look something like this:
```swift
import HTTP
drop.get("pokemon", Pokemon.self) { request, pokemon in
let response = Response()
response.pokemon = pokemon
return response
}
```
Or, if you want to go a step further, you can make `Pokemon` conform to `ResponseRepresentable`.
```swift
import HTTP
extension Pokemon: ResponseRepresentable {
func makeResponse() throws -> Response {
let response = Response()
response.pokemon = self
return response
}
}
```
Now your route closures are greatly simplified and you don't need to `import HTTP`.
```swift
drop.get("pokemon", Pokemon.self) { request, pokemon in
return pokemon
}
```
Middleware is incredibly powerful. Combined with extensions, it allows you to add functionality that feels native to the framework.
For those that are curious, this is how Vapor manages JSON internally. Whenever you return JSON in a closure, it sets the `json: JSON?` property on `Response`. The `JSONMiddleware` then detects this property and serializes the JSON into the body of the response.

View File

@ -1,130 +0,0 @@
---
currentMenu: guide-provider
---
# Provider
The `Provider` protocol creates a simple and predictable way for adding functionality and third party packages to your Vapor project.
## Adding a Provider
Adding a provider to your application takes 2-3 steps.
### Add Package
All of Vapor's providers end with the `-provider` syntax. You can see a list of [available providers](https://github.com/vapor?utf8=✓&q=-provider) by searching on our GitHub.
To add the provider to your package, add it as a dependency in your `Package.swift` file.
```swift
let package = Package(
name: "MyApp",
dependencies: [
.Package(url: "https://github.com/vapor/vapor.git", majorVersion: 1, minor: 0),
.Package(url: "https://github.com/vapor/mysql-provider.git", majorVersion: 1, minor: 0)
]
)
```
> It's important to `vapor clean` or `vapor build --clean` after adding new packages.
### Import
Once the provider has been added, you can import it using `import VaporFoo` where `Foo` is the name of the provider.
Here is what importing the MySQL provider looks like:
```swift
import Vapor
import VaporMySQL
let drop = Droplet()
try drop.addProvider(VaporMySQL.Provider.self)
// ...
drop.run()
```
Every provider comes with a class named `Provider`. Append the `Type` of this class to your `providers` array in the `Droplet`'s init method.
### Config
Some drivers may require a configuration file. For example, `VaporMySQL` requires a `Config/mysql.json` file like the following:
```json
{
"host": "localhost",
"user": "root",
"password": "",
"database": "vapor"
}
```
You will receive an error during the `Droplet`'s initialization if a configuration file is required.
## Advanced
You may choose to initialize the provider yourself.
```swift
import Vapor
import VaporMySQL
let drop = Droplet()
let mysql = try VaporMySQL.Provider(host: "localhost", user: "root", password: "", database: "vapor")
drop.addProvider(mysql)
...
drop.run()
```
## Create a Provider
Creating a provider is easy, you just need to create a package with a class `Provider` that conforms to `Vapor.Provider`.
### Example
Here is what a provider for an example `Foo` package would look like. All the provider does is take a message, then print the message when the `Droplet` starts.
```swift
import Vapor
public final class Provider: Vapor.Provider {
public let message: String
public let provided: Providable
public convenience init(config: Config) throws {
guard let message = config["foo", "message"].string else {
throw SomeError
}
try self.init(message: message)
}
public init(message: String) throws {
self.message = message
}
public func afterInit(_ drop: Droplet) {
}
public func beforeServe(_ drop: Droplet) {
drop.console.info(message)
}
}
```
This provider wil require a `Config/foo.json` file that looks like:
```json
{
"message": "The message to output"
}
```
The provider can also be initialized manually with the `init(message: String)` init.

View File

@ -1,77 +0,0 @@
---
currentMenu: guide-sessions
---
# Sessions
Sessions help you store information about a user between requests. As long as the client supports cookies, sessions are easy to create.
## Middleware
Enable sessions on your `Droplet` by adding an instance of `SessionMiddleware`.
```swift
import Sessions
let memory = MemorySessions()
let sessions = SessionsMiddleware(sessions: memory)
```
Then add to the `Droplet`.
```
let drop = Droplet()
drop.middleware.append(sessions)
```
> Note: If you'd like to enable or disable the middleware based on config files, check out [middleware](../guide/middleware.md).
## Request
After `SessionMiddleware` has been enabled, you can access the `req.sessions()` method to get access to session data.
```swift
let data = try req.session().data
```
## Example
Let's create an example that remembers the user's name.
### Store
```swift
drop.post("remember") { req in
guard let name = req.data["name"]?.string else {
throw Abort.badRequest
}
try req.session().data["name"] = Node.string(name)
return "Remebered name."
}
```
On `POST /remember`, fetch a `name` from the request input, then store this name into the session data.
### Fetch
On `GET /remember`, fetch the `name` from the session data and return it.
```swift
drop.get("remember") { req in
guard let name = try req.session().data["name"]?.string else {
return throw Abort.custom(status: .badRequest, message: "Please POST the name first.")
}
return name
}
```
## Cookie
The session will be stored using the `vapor-session` cookie.

View File

@ -1,106 +0,0 @@
---
currentMenu: guide-validation
---
# Validation
Vapor provides a few different ways to validate data coming into your application. Let's start by looking at the most common.
## Common Usage
Several useful convenience validators are included by default. You can use these to validate data coming into your application, or combine them and create your own.
Let's look at the most common way to validate data.
```swift
class Employee {
var email: Valid<Email>
var name: Valid<Name>
init(request: Request) throws {
name = try request.data["name"].validated()
email = try request.data["email"].validated()
}
}
```
Here we have a typical Employee model with an `email` and `name` property. By declaring both of these properties as `Valid<>`, you are ensuring that these properties can only ever contain valid data. The Swift type checking system will prevent anything that does not pass validation from being stored.
To store something in a `Valid<>` property, you must use the `.validated()` method. This is available for any data returned by `request.data`.
`Email` is a real `validator` included with Vapor, but `Name` is not. Let's take a look at how you can create a Validator.
```swift
Valid<OnlyAlphanumeric>
Valid<Email>
Valid<Unique<T>>
Valid<Matches<T>>
Valid<In<T>>
Valid<Contains<T>>
Valid<Count<T>>
```
## Validators vs. ValidationSuites
Validators, like `Count` or `Contains` can have multiple configurations. For example:
```swift
let name: Valid<Count<String>> = try "Vapor".validated(by: Count.max(5))
```
Here we are validating that the `String` is at most 5 characters long. The type of `Valid<Count>` tells us that the string has been validated to be a certain count, but it does not tell us exactly what that count was. The string could have been validated to be less than three characters or more than one million.
Because of this, `Validators` themselves are not as type safe as some applications might desire. `ValidationSuites` fix this. They combine multiple `Validators` and/or `ValidationSuites` together to represent exactly what type of data should be considered valid.
## Custom Validator
Here is how to create a custom `ValidationSuite`.
```swift
class Name: ValidationSuite {
static func validate(input value: String) throws {
let evaluation = OnlyAlphanumeric.self
&& Count.min(5)
&& Count.max(20)
try evaluation.validate(input: value)
}
}
```
You only have to implement one method. In this method, use any other validators or logic to create your custom validator. Here we are defining a `Name` as only accepting alphanumeric Strings that are between 5 and 20 characters.
Now we can be sure that anything of type `Valid<Name>` follows these rules.
## Combining Validators
In the `Name` validator, you can see that `&&` is being used to combine validators. You can use `&&` as well as `||` to combine any validator as you would boolean values with an `if` statement.
You can also use `!` to invert the validator.
```swift
let symbols = input.validated(by: !OnlyAlphanumeric.self)
```
## Testing Validity
While `validated() throw` is the most common method for validating, there are two others.
```swift
let passed = input.passes(Count.min(5))
let valid = try input.tested(Count.min(5))
```
`passes()` returns a boolean indicating whether or not the test passed. `tested()` will throw if the validation does not pass. But unlike `validated()` which returns a `Valid<>` type, `tested()` returns the original type of the item it was called on.
## Validation Failures
Vapor will automatically catch validation failures in the `ValidationMiddleware`. But you can catch them on your own, or customize responses for certain types of failures.
```swift
do {
//validation here
} catch let error as ValidationErrorProtocol {
print(error.message)
}
```

View File

@ -1,54 +0,0 @@
---
currentMenu: guide-views
---
# Views
Views return HTML data from your application. They can be created from pure HTML documents or passed through renderers such as Mustache or Stencil.
## Views Directory
Views are stored in `Resources/Views`. They are created by calling the `view` method on `Droplet`.
## HTML
Returning HTML, or any other non-rendered document, is simple. Just use the path of the document relative to the views directory.
```swift
drop.get("html") { request in
return try drop.view.make("index.html")
}
```
## Templating
Templated documents like [Leaf](./leaf.html), Mustache, or Stencil can take a `Context`.
```swift
drop.get("template") { request in
return try drop.view.make("welcome", [
"message": "Hello, world!"
])
}
```
## Public Resources
Any resources that your views need, such as images, styles, and scripts, should be placed in the `Public` folder at the root of your application.
## View Renderer
Any class that conforms to `ViewRenderer` can be added to our droplet.
```swift
let drop = Droplet()
drop.view = LeafRenderer(viewsDir: drop.viewsDir)
```
## Available Renderers
These renderers can be added to your application through [Providers](provider.md).
- [Leaf](https://github.com/vapor/leaf)
- [Mustache](https://github.com/vapor/mustache-provider)

View File

@ -1,141 +0,0 @@
---
currentMenu: http-body
---
> Module: `import HTTP`
# Body
The `HTTP.Body` represents the payload of an `HTTP.Message`, and is used to pass the underlying data. Some examples of this in practice would be `JSON`, `HTML` text, or the bytes of an image. Let's look at the implementation:
```swift
public enum Body {
case data(Bytes)
case chunked((ChunkStream) throws -> Void)
}
```
## Data Case
The `data` case is by far the most common use for a `Body` in an `HTTP.Message`. It is simply an array of bytes. The serialization protocol or type associated with these bytes is usually defined by the `Content-Type` header. Let's look at some examples.
### Application/JSON
If our `Content-Type` header contains `application/json`, then the underlying bytes represent serialized JSON.
```swift
if let contentType = req.headers["Content-Type"], contentType.contains("application/json"), let bytes = req.body.bytes {
let json = try JSON(bytes: bytes)
print("Got JSON: \(json)")
}
```
### Image/PNG
If our `Content-Type` contains `image/png`, then the underlying bytes represent an encoded png.
```swift
if let contentType = req.headers["Content-Type"], contentType.contains("image/png"), let bytes = req.body.bytes {
try database.save(image: bytes)
}
```
## Chunked Case
The `chunked` case only applies to outgoing `HTTP.Message`s in Vapor. It is traditionally a responder's role to collect an entire chunked encoding before passing it on. We can use this to send a body asynchronously.
```swift
let body: Body = Body.chunked(sender)
return Response(status: .ok, body: body)
```
We can implement this manually, or use Vapor's built in convenience initializer for chunked bodies:
```swift
return Response(status: .ok) { chunker in
for name in ["joe", "pam", "cheryl"] {
sleep(1)
try chunker.send(name)
}
try chunker.close()
}
```
> Make sure to call `close()` before the chunker leaves scope.
## BodyRepresentable
In addition to the concrete `Body` type, as is common in Vapor, we also have wide support for `BodyRepresentable`. This means objects that we're commonly converting to `Body` type can be used interchangeably. For example:
```swift
return Response(body: "Hello, World!")
```
In the above example, string is converted to bytes and added to the body.
> In practice, it is better to use `return "Hello, World!"`. Vapor will automatically be able to set the `Content-Type` to appropriate values.
Let's look at how it's implemented:
```swift
public protocol BodyRepresentable {
func makeBody() -> Body
}
```
### Custom
We can conform our own types to this as well where applicable. Let's pretend we have a custom data type, `.vpr`. Let's conform our `VPR` file type model:
```swift
extension VPRFile: HTTP.BodyRepresentable {
func makeBody() -> Body {
// collect bytes
return .data(bytes)
}
}
```
> You may have noticed above, that the protocol throws, but our implementation does not. This is completely valid in Swift and will allow you to not throw if you're ever calling the function manually.
Now we're able to include our `VPR` file directly in our `Responses`.
```swift
drop.get("files", ":file-name") { request in
let filename = try request.parameters.extract("file-name") as String
let file = VPRFileManager.fetch(filename)
return Response(status: .ok, headers: ["Content-Type": "file/vpr"], body: file)
}
```
In practice, if we're repeating this often, we'll probably conform `VPRFile` directly to `ResponseRepresentable`
```swift
extension VPRFile: HTTP.ResponseRepresentable {
func makeResponse() -> Response {
return Response(
status: .ok,
headers: ["Content-Type": "file/vpr"],
body: file
)
}
}
```
Here's our above example now:
```swift
drop.get("files", ":file-name") { request in
let filename = try request.parameters.extract("file-name") as String
return VPRFileManager.fetch(filename)
}
```
We could also use type-safe routing to make this even more concise:
```swift
drop.get("files", String.self) { request, filename in
return VPRFileManager.fetch(filename)
}
```

View File

@ -1,160 +0,0 @@
---
currentMenu: http-client
---
> Module: `import HTTP`
# Client
The client provided by `HTTP` is used to make outgoing requests to remote servers. Let's look at a simple outgoing request.
## QuickStart
Let's jump right in to make a simple HTTP Request. Here's a basic `GET` request using your Vapor `Droplet`.
```swift
let query = ...
let spotifyResponse = try drop.client.get("https://api.spotify.com/v1/search?type=artist&q=\(query)")
print(spotifyR)
```
### Clean Up
The url above can be a little tricky to read, so let's use the query parameter to clean it up a little bit:
```swift
try drop.client.get("https://api.spotify.com/v1/search", query: ["type": "artist", "q": query])
```
### Continued
In addition to `GET` requests, Vapor's client provides support for most common HTTP functions. `GET`, `POST`, `PUT`, `PATCH`, `DELETE`
### POST as json
```swift
try drop.client.post("http://some-endpoint/json", headers: ["Content-Type": "application/json"], body: myJSON.makeBody())
```
### POST as x-www-form-urlencoded
```swift
try drop.client.post("http://some-endpoint", headers: [
"Content-Type": "application/x-www-form-urlencoded"
], body: Body.data( Node(node: [
"email": "mymail@vapor.codes"
]).formURLEncoded()))
```
### Full Request
To access additional functionality or custom methods, use the underlying `request` function directly.
```swift
public static func get(_ method: Method,
_ uri: String,
headers: [HeaderKey: String] = [:],
query: [String: CustomStringConvertible] = [:],
body: Body = []) throws -> Response
```
For example:
```swift
try drop.client.request(.other(method: "CUSTOM"), "http://some-domain", headers: ["My": "Header"], query: ["key": "value"], body: [])
```
## Config
The `Config/clients.json` file can be used to modify the client's settings.
### TLS
Host and certificate verification can be disabled.
> Note: Use extreme caution when modifying these settings.
```json
{
"tls": {
"verifyHost": false,
"verifyCertificates": false
}
}
```
### Mozilla
The Mozilla certificates are included by default to make fetching content from secure sites easy.
```json
{
"tls": {
"certificates": "mozilla"
}
}
```
## Advanced
In addition to our Droplet, we can also use and interact with the `Client` manually. Here's how our default implementation in Vapor looks:
```swift
let response = try Client<TCPClientStream>.get("http://some-endpoint/mine")
```
The first thing we likely noticed is `TCPClientStream` being used as a Generic value. This will be the underlying connection that the `HTTP.Client` can use when performing the request. By conforming to the underlying `ClientStream`, an `HTTP.Client` can accept custom stream implementations seamlessly.
## Save Connection
Up to this point, we've been interacting with the Client via `class` or `static` level functions. This allows us to end the connection upon a completed request and is the recommended interaction for most use cases. For some advanced situations, we may want to reuse a connection. For these, we can initialize our client and perform multiple requests like this.
```swift
let pokemonClient = try drop?.client.make(scheme: "http", host: "pokeapi.co")
for i in 0...1 {
let response = try pokemonClient?.get(path: "/api/v2/pokemon/", query: ["limit": 20, "offset": i])
print("response: \(response)")
}
```
## ClientProtocol
Up to this point, we've focused on the built in `HTTP.Client`, but users can also include their own customized clients by conforming to `HTTP.ClientProtocol`. Let's look at the implementation:
```swift
public protocol Responder {
func respond(to request: Request) throws -> Response
}
public protocol Program {
var host: String { get }
var port: Int { get }
var securityLayer: SecurityLayer { get }
// default implemented
init(host: String, port: Int, securityLayer: SecurityLayer) throws
}
public protocol ClientProtocol: Program, Responder {
var scheme: String { get }
var stream: Stream { get }
init(scheme: String, host: String, port: Int, securityLayer: SecurityLayer) throws
}
```
By conforming to these underlying functions, we immediately gain access to the public `ClientProtocol` apis we viewed above.
## Customize Droplet
If we've introduced a custom conformance to `HTTP.ClientProtocol`, we can pass this into our droplet without changing the underlying behavior in our application.
For example:
```swift
let drop = Droplet()
drop.client = MyCustomClient.self
```
Going forward, all of your calls to `drop.client` will use `MyCustomClient.self`:
```swift
drop.client.get(... // uses `MyCustomClient`
```

View File

@ -1,94 +0,0 @@
---
currentMenu: http-cors
---
# CORS
Vapor by default provides a middleware for implementing proper support for Cross-Origin Resource Sharing (CORS) named `CORSMiddleware`.
"Cross-Origin Resource Sharing (CORS) is a specification that enables truly open access across domain-boundaries. If you serve public content, please consider using CORS to open it up for universal JavaScript / browser access." - [http://enable-cors.org/](http://enable-cors.org/)
To learn more about middlewares, please visit the Middleware section of the documentation [here](https://vapor.github.io/documentation/guide/middleware.html).
![](https://upload.wikimedia.org/wikipedia/commons/c/ca/Flowchart_showing_Simple_and_Preflight_XHR.svg)
*Image Author: [Wikipedia](https://commons.wikimedia.org/wiki/File:Flowchart_showing_Simple_and_Preflight_XHR.svg)*
## Basic
First of all, add the CORS middleware into your droplet middlewares array.
```swift
# Insert CORS before any other middlewares
drop.middleware.insert(CORSMiddleware(), at: 0)
```
> Note: Make sure you insert CORS middleware before any other throwing middlewares, like the AbortMiddleware or similar. Otherwise the proper headers might not be added to the response.
`CORSMiddleware` has a default configuration which should suit most users, with values as follows:
- **Allowed Origin**
- Value of origin header in the request.
- **Allowed Methods**
- `GET`, `POST`, `PUT`, `OPTIONS`, `DELETE`, `PATCH`
- **Allowed Headers**
- `Accept`, `Authorization`, `Content-Type`, `Origin`, `X-Requested-With`
## Advanced
All settings and presets can be customized by advanced users. There's two ways of doing this, either you programatically create and configure a `CORSConfiguration` object or you can put your configuration into a Vapor's JSON config file.
See below for how to set up both and what are the options.
### Configuration
The `CORSConfiguration` struct is used to configure the `CORSMiddleware`. You can instanitate one like this:
```swift
let configuration = CORSConfiguration(allowedOrigin: .custom("https://vapor.codes"),
allowedMethods: [.get, .post, .options],
allowedHeaders: ["Accept", "Authorization"],
allowCredentials: false,
cacheExpiration: 600,
exposedHeaders: ["Cache-Control", "Content-Language"])
```
After creating a configuration you can add the CORS middleware.
```swift
drop.middleware.insert(CORSMiddleware(configuration: configuration), at: 0)
```
> Note: Please consult the documentation in the source code of the `CORSConfiguration` for more information about available values for the settings.
### JSON Config
Optionally, `CORSMiddleware` can be configured using the Vapor's `Config` which is created out of the json files contained in your Config folder. You will need to create a file called `cors.json` or `CORS.json` in your Config folder in your project and add the required keys.
Example of how such a file could look as follows:
```swift
{
"allowedOrigin": "origin",
"allowedMethods": "GET,POST,PUT,OPTIONS,DELETE,PATCH",
"allowedHeaders": ["Accept", "Authorization", "Content-Type", "Origin", "X-Requested-With"]
}
```
> Note: Following keys are required: `allowedOrigin`, `allowedMethods`, `allowedHeaders`. If they are not present an error will be thrown while instantiating the middleware.
>
> Optionally you can also specify the keys `allowCredentials` (Bool), `cacheExpiration` (Int) and `exposedHeaders` ([String]).
Afterwards you can add the middleware using the a throwing overload of the initialiser that accepts Vapor's `Config`.
```swift
let drop = Droplet()
do {
drop.middleware.insert(try CORSMiddleware(configuration: drop.config), at: 0)
} catch {
fatalError("Error creating CORSMiddleware, please check that you've setup cors.json correctly.")
}
```

View File

@ -1,223 +0,0 @@
---
currentMenu: http-request
---
> Module: `import HTTP`
# Request
The most common part of the `HTTP` library we'll be interacting with is the `Request` type. Here's a look at some of the most commonly used attributes in this type.
```swift
public var method: Method
public var uri: URI
public var parameters: Node
public var headers: [HeaderKey: String]
public var body: Body
public var data: Content
```
### Method
The HTTP `Method` associated with the `Request`, ie: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`.
### URI
The associated `URI` of the request. We will use this to access attributes about the `uri` the request was sent to.
For example, given the following uri: `http://vapor.codes/example?query=hi#fragments-too`
```swift
let scheme = request.uri.scheme // http
let host = request.uri.host // vapor.codes
let path = request.uri.path // /example
let query = request.uri.query // query=hi
let fragment = request.uri.fragment // fragments-too
```
### Route Parameters
The url parameters associated with the request. For example, if we have a path registered as `hello/:name/age/:age`, we would be able to access those in our request, like so:
```swift
let name = request.parameters["name"] // String?
let age = request.parameters["age"]?.int // Int?
```
Or, to automatically throw on `nil` or invalid variable, you can also `extract`
```swift
let name = try request.parameters.extract("name") as String
let age = try request.parameters.extract("age") as Int
```
These extract functions can cast to any `NodeInitializable` type, including your own custom types. Make sure to check out [Node](https://github.com/vapor/node) for more info.
> Note: Vapor also provides type safe routing in the routing section of our docs.
### Headers
These are the headers associated with the request. If you are preparing an outgoing request, this can be used to add your own keys.
```swift
let contentType = request.headers["Content-Type"]
```
Or for outgoing requests:
```swift
let request = Request ...
request.headers["Content-Type"] = "application/json"
request.headers["Authorization"] = ... my auth token
```
#### Extending Headers
We generally seek to improve code bases by removing stringly typed code where possible. We can add variables to the headers using generic extensions.
```swift
extension HTTP.KeyAccessible where Key == HeaderKey, Value == String {
var customKey: String? {
get {
return self["Custom-Key"]
}
set {
self["Custom-Key"] = newValue
}
}
}
```
With this pattern implemented, our string `"Custom-Key"` is contained in one section of our code. We can now access like this:
```swift
let customKey = request.headers.customKey
// or
let request = ...
request.headers.customKey = "my custom value"
```
### Body
This is the body associated with the request and represents the general data payload. You can view more about body in the associated [docs](./body.md)
For incoming requests, we'll often pull out the associated bytes like so:
```swift
let rawBytes = request.body.bytes
```
## Content
Generally when we're sending or receiving requests, we're using them as a way to transport content. For this, Vapor provides a convenient `data` variable associated with the request that prioritizes content in a consistent way.
For example, say I receive a request to `http://vapor.codes?hello=world`.
```swift
let world = request.data["hello"]?.string
```
This same code will work if I receive a JSON request, for example:
```json
{
"hello": "world"
}
```
Will still be accessible through data.
```swift
let world = request.data["hello"]?.string
```
> Note: Force unwrap should never be used.
This also applies to multi-part requests and can even be extended to new types such as XML or YAML through middleware.
If you'd prefer to access given types more explicitly, that's totally fine. The `data` variable is purely opt-in convenience for those who want it.
## JSON
To access JSON directly on a given request, use the following:
```swift
let json = request.json["hello"]
```
## Query Parameters
The same applies to query convenience:
```swift
let query = request.query?["hello"] // String?
let name = request.query?["name"]?.string // String?
let age = request.query?["age"]?.int // Int?
let rating = request.query?["rating"]?.double // Double?
```
## Key Paths
Key paths work on most Vapor types that can have nested key value objects. Here's a couple examples of how to access given the following json:
```json
{
"metadata": "some metadata",
"artists" : {
"href": "http://someurl.com",
"items": [
{
"name": "Van Gogh",
},
{
"name": "Mozart"
}
]
}
}
```
We could access the data in the following ways:
### Metadata
Access top level values
```swift
let type = request.data["metadata"].string // "some metadata"
```
### Items
Access nested values
```swift
let items = request.data["artists", "items"] // [["name": "Van Gogh"], ["name": "Mozart"]]
```
### Mixing Arrays and Objects
Get first artists
```swift
let first = request.data["artists", "items", 0] // ["name": "Van Gogh"]
```
### Array Item
Get key from array item
```swift
let firstName = request.data["artists", "items", 0, "name"] // "Van Gogh"
```
### Array Comprehension
We can also smartly map an array of keys, for example, to just get the names of all of the artists, we could use the following
```swift
let names = request.data["artists", "items", "name"] // ["Van Gogh", "Mozart"]
```

View File

@ -1,79 +0,0 @@
---
currentMenu: http-responder
---
> Module: `import HTTP`
# Responder
The `Responder` is a simple protocol defining the behavior of objects that can accept a `Request` and return a `Response`. Most notably in Vapor, it is the core API endpoint that connects the `Droplet` to the `Server`. Let's look at the definition:
```swift
public protocol Responder {
func respond(to request: Request) throws -> Response
}
```
> The responder protocol is most notably related to Droplet and it's relationship with a server. Average users will not likely interact with it much.
## Simple
Of course, Vapor provides some conveniences for this, and in practice, we will often call:
```swift
try drop.run()
```
## Manual
As we just mentioned, the Vapor `Droplet` itself conforms to `Responder`, connecting it to the `Server`. This means if we wanted to serve our droplet manually, we could do:
```swift
let server = try Server<TCPServerStream, Parser<Request>, Serializer<Response>>(port: port)
try server.start(responder: droplet) { error in
print("Got error: \(error)")
}
```
## Advanced
We can conform our own objects to `Responder` and pass them to `Servers`. Let's look at an example:
```swift
final class Responder: HTTP.Responder {
func respond(to request: Request) throws -> Response {
let body = "Hello World".makeBody()
return Response(body: body)
}
}
```
This only returns `"Hello World"` for every request, it's most commonly going to be linked with a router of some type.
```swift
final class Responder: HTTP.Responder {
let router: Router = ...
func respond(to request: Request) throws -> Response {
return try router.route(request)
}
}
```
We'll then pass this responder to a server and let it go.
```swift
let server = try Server<TCPServerStream, Parser<Request>, Serializer<Response>>(port: port)
print("visit http://localhost:\(port)/")
try server.start(responder: Responder()) { error in
print("Got error: \(error)")
}
```
This can be used as a jumping off point for applications looking to implement features manually.
## Client
The `HTTP.Client` is itself a `Responder` although, instead of handling the `Request` itself, it passes it on to the underlying URI.

View File

@ -1,104 +0,0 @@
---
currentMenu: http-response-representable
---
> Module: `import HTTP`
# ResponseRepresentable
Traditionally HTTP servers take a `Request` and return a `Response`. Vapor is no different, but we can take advantage of Swift's powerful protocols to be a bit more flexible to the user facing API.
Let's start with the definition of `ResponseRepresentable`
```swift
public protocol ResponseRepresentable {
func makeResponse() throws -> Response
}
```
By conforming to this protocol, we can more flexibly return things that conform instead of creating the response manually each time. Vapor provides some of these by default. Including (but not limited to):
### String
Because string conforms to `ResponseRepresentable`, we can return it directly in a Vapor route handler.
```swift
drop.get("hello") { request in
return "Hello, World!"
}
```
### JSON
`JSON` can be returned directly instead of recreating a response each time.
```swift
drop.get("hello") { request in
return try JSON(node: [
"hello": "world",
"some-numbers": [
1,
2,
3
]
]
)
}
```
### Response
Of course, we can also return Responses for anything not covered:
```swift
drop.get("hello") { request in
return Response(status: .ok, headers: ["Content-Type": "text/plain"], body: "Hello, World!")
}
```
## Conforming
All we need to do to return our own objects is conform them to `ResponseRepresentable`. Let's look at an example type, a simple blog post model:
```swift
import Foundation
struct BlogPost {
let id: String
let content: String
let createdAt: NSDate
}
```
And now, let's conform it to response representable.
```swift
import HTTP
import Foundation
extension BlogPost: ResponseRepresentable {
func makeResponse() throws -> Response {
let json = try JSON(node:
[
"id": id,
"content": content,
"created-at": createdAt.timeIntervalSince1970
]
)
return try json.makeResponse()
}
}
```
> Don't forget to import HTTP.
Now that we've modeled our BlogPost, we can return it directly in route handlers.
```swift
drop.post("post") { req in
guard let content = request.data["content"] else { throw Error.missingContent }
let post = Post(content: content)
try post.save(to: database)
return post
}
```

View File

@ -1,126 +0,0 @@
---
currentMenu: http-response
---
> Module: `import HTTP`
# Response
When building endpoints, we'll often be returning responses for requests. If we're making outgoing requests, we'll be receiving them.
```swift
public let status: Status
public var headers: [HeaderKey: String]
public var body: Body
public var data: Content
```
#### Status
The http status associated with the event, for example `.ok` == 200 ok.
#### Headers
These are the headers associated with the request. If you are preparing an outgoing response, this can be used to add your own keys.
```swift
let contentType = response.headers["Content-Type"]
```
Or for outgoing response:
```swift
let response = response ...
response.headers["Content-Type"] = "application/json"
response.headers["Authorization"] = ... my auth token
```
##### Extending Headers
We generally seek to improve code bases by removing stringly typed code where possible. We can add variables to the headers using generic extensions.
```swift
extension HTTP.KeyAccessible where Key == HeaderKey, Value == String {
var customKey: String? {
get {
return self["Custom-Key"]
}
set {
self["Custom-Key"] = newValue
}
}
}
```
With this pattern implemented, our string `"Custom-Key"` is contained in one section of our code. We can now access like this:
```swift
let customKey = response.headers.customKey
// or
let request = ...
response.headers.customKey = "my custom value"
```
#### Body
This is the body associated with the response and represents the general data payload. You can view more about body in the associated [docs](./body.md)
For responses, the body is most commonly set at initialization. With two main types.
##### BodyRepresentable
Things that can be converted to bytes, ie:
```swift
let response = Response(status: .ok, body: "some string")
```
In the above example, the `String` will be automatically converted to a body. Your own types can do this as well.
##### Bytes Directly
If we already have our bytes array, we can pass it into the body like so:
```swift
let response = Response(status: .ok, body: .data(myArrayOfBytes))
```
##### Chunked
To send an `HTTP.Response` in chunks, we can pass a closure that we'll use to send our response body in parts.
```swift
let response = Response(status: .ok) { chunker in
for name in ["joe", "pam", "cheryl"] {
sleep(1)
try chunker.send(name)
}
try chunker.close()
}
```
> Make sure to call `close()` before the chunker leaves scope.
## Content
We can access content the same we do in a [request](./request.md). This most commonly applies to outgoing requests.
```swift
let pokemonResponse = try drop.client.get("http://pokeapi.co/api/v2/pokemon/")
let names = pokemonResponse.data["results", "name"]?.array
```
## JSON
To access JSON directly on a given response, use the following:
```swift
let json = request.response["hello"]
```
## Key Paths
For more on KeyPaths, visit [here](./request.md#key-paths)

View File

@ -1,210 +0,0 @@
---
currentMenu: http-server
---
# Server
The server is responsible for accepting connections from clients, parsing their requests, and delivering them a response.
## Default
Starting your Droplet with a default server is simple.
```swift
import Vapor
let drop = Droplet()
drop.run()
```
The default server will bind to host `0.0.0.0` at port `8080`.
## Config
If you are using a `Config/servers.json` file, this is where you can easily change your host and port or even boot multiple servers.
```json
{
"default": {
"port": "$PORT:8080",
"host": "0.0.0.0",
"securityLayer": "none"
}
}
```
The default `servers.json` is above. The port with try to resolve the environment variable `$PORT` or fallback to `8080`.
### Multiple
You can start multiple servers in the same application. This is especially useful if you want to boot an `HTTP` and `HTTPS` server side by side.
```json
{
"plaintext": {
"port": "80",
"host": "vapor.codes",
"securityLayer": "none"
},
"secure": {
"port": "443",
"host": "vapor.codes",
"securityLayer": "tls",
"tls": {
"certificates": "none",
"signature": "selfSigned"
}
},
}
```
## TLS
TLS (formerly SSL) can be configured with a variety of different certificate and signature types.
### Verify
Verificiation of hosts and certificates can be disabled. They are enabled by default.
> Note: Be extremely careful when disabling these options.
```json
"tls": {
"verifyHost": false,
"verifyCertificates": false
}
```
### Certificates
#### None
```json
"tls": {
"certificates": "none"
}
```
#### Chain
```json
"tls": {
"certificates": "chain",
"chainFile": "/path/to/chainfile"
}
```
#### Files
```json
"tls": {
"certificates": "files",
"certificateFile": "/path/to/cert.pem",
"privateKeyFile": "/path/to/key.pem"
}
```
#### Certificate Authority
```json
"tls": {
"certificates": "ca"
}
```
### Signature
#### Self Signed
```json
"tls": {
"signature": "selfSigned"
}
```
#### Signed File
```json
"tls": {
"signature": "signedFile",
"caCertificateFile": "/path/to/file"
}
```
#### Signed Directory
```json
"tls": {
"signature": "signedDirectory",
"caCertificateDirectory": "/path/to/dir"
}
```
## Example
Here is an example `servers.json` file using certificate files with a self signed signature and host verification redundantly set to `true`.
```json
{
"secure": {
"port": "8443",
"host": "0.0.0.0",
"securityLayer": "tls",
"tls": {
"verifyHost": true,
"certificates": "files",
"certificateFile": "/vapor/certs/cert.pem",
"privateKeyFile": "/vapor/certs/key.pem",
"signature": "selfSigned"
}
}
}
```
## Manual
Servers can also be configured manually, without configuration files.
> Note: If servers are configured programatically, they override any config settings.
### Simple
The `run` method on the Droplet takes a dictionary of server configuration objects. The key is the name of the server.
```swift
import Vapor
let drop = Droplet()
drop.run(servers: [
"default": (host: "vapor.codes", port: 8080, securityLayer: .none)
]
```
### TLS
TLS can also be configured manually, and works similarly to the `servers.json` config files described above.
```swift
import Vapor
import TLS
let drop = Droplet()
let config = try TLS.Config(
mode: .server,
certificates: .files(
certificateFile: "/Users/tanner/Desktop/certs/cert.pem",
privateKeyFile: "/Users/tanner/Desktop/certs/key.pem",
signature: .selfSigned
),
verifyHost: true,
verifyCertificates: true
)
drop.run(servers: [
"plaintext": ("vapor.codes", 8080, .none),
"secure": ("vapor.codes", 8443, .tls(config)),
])
````

View File

@ -1,146 +0,0 @@
---
currentMenu: routing-basic
---
# Basic Routing
Routing is one of the most critical parts of a web framework. The router decides which requests get which responses.
Vapor has a plethora of functionality for routing including route builders, groups, and collections. In this section, we will look at the basics of routing.
## Register
The most basic route includes a method, path, and closure.
```swift
drop.get("welcome") { request in
return "Hello"
}
```
The standard HTTP methods are available including `get`, `post`, `put`, `patch`, `delete`, and `options`.
```swift
drop.post("form") { request in
return "Submitted with a POST request"
}
```
You can also use `any` to match all methods.
## Nesting
To nest paths (adding `/`s in the URL), simply add commas.
```swift
drop.get("foo", "bar", "baz") { request in
return "You requested /foo/bar/baz"
}
```
You can also use `/`, but commas are often easier to type and work better with type safe route [parameters](parameters.md).
## Alternate
An alternate syntax that accepts a `Method` as the first parameter is also available.
```swift
drop.add(.trace, "welcome") { request in
return "Hello"
}
```
This may be useful if you want to register routes dynamically or use a less common method.
## Request
Each route closure is given a single [Request](../http/request.md). This contains all of the data associated with the request that led to your route closure being called.
## Response Representable
A route closure can return in three ways:
- `Response`
- `ResponseRepresentable`
- `throw`
### Response
A custom [Response](../http/response.md) can be returned.
```swift
drop.get("vapor") { request in
return Response(redirect: "http://vapor.codes")
}
```
This is useful for creating special responses like redirects. It is also useful for cases where you want to add cookies or other items to the response.
### Response Representable
As you have seen in the previous examples, `String`s can be returned in route closures. This is because they conform to [ResponseRepresentable](../http/response-representable.md)
A lot of types in Vapor conform to this protocol by default:
- String
- Int
- JSON
- Model
```swift
drop.get("json") { request in
return try JSON(node: [
"number": 123,
"text": "unicorns",
"bool": false
])
}
```
> If you are curious about what `node:` means, read more about [Node](https://github.com/vapor/node)
### Throwing
If you are unable to return a response, you may `throw` any object that conforms to `Error`. Vapor comes with a default error enum `Abort`.
```swift
drop.get("404") { request in
throw Abort.notFound
}
```
You can customize the message of these errors by using `Abort`
```swift
drop.get("error") { request in
throw Abort.custom(status: .badRequest, message: "Sorry 😱")
}
```
These errors are caught by default in the `AbortMiddleware` where they are turned into a JSON response like the following.
```json
{
error: true,
message: "<the message>"
}
```
If you want to override this behavior, remove the `AbortMiddleware` from the `Droplet`'s middleware and add your own.
## Fallback
Fallback routes allow you to match multiple layers of nesting slashes.
```swift
app.get("anything", "*") { request in
return "Matches anything after /anything"
}
```
For example, the above route matches all of the following and more:
- /anything
- /anything/foo
- /anything/foo/bar
- /anything/foo/bar/baz
- ...

View File

@ -1,82 +0,0 @@
---
currentMenu: routing-collection
---
# Route Collections
Route collections allow multiple routes and route groups to be organized in different files or modules.
## Example
Here is an example of a route collection for the `v1` portion of an API.
```swift
import Vapor
import HTTP
import Routing
class V1Collection: RouteCollection {
typealias Wrapped = HTTP.Responder
func build<B: RouteBuilder>(_ builder: B) where B.Value == Wrapped {
let v1 = builder.grouped("v1")
let users = v1.grouped("users")
let articles = v1.grouped("articles")
users.get { request in
return "Requested all users."
}
articles.get(Article.self) { request, article in
return "Requested \(article.name)"
}
}
}
```
This class could be place in any file, and we could add it to our droplet or even another route group.
```swift
let v1 = V1Collection()
drop.collection(v1)
```
The `Droplet` will then be passed to the `build(_:)` method of your route collection and have the various routes added to it.
### Breakdown
For those that are curious, let's break down the route collection line by line to better understand what is going on.
```swift
typealias Wrapped = HTTP.Responder
```
This limits our route collection to adding HTTP responders. While the underlying router is capable of routing any type, Vapor routes HTTP responders exclusively. If we want to use this route collection with Vapor, it's wrapped type needs to match.
```swift
func build<B: RouteBuilder>(_ builder: B) where B.Value == Wrapped {
```
This method accepts a route builder and also verifies that the route builder accepts `Wrapped` or, as defined in the last line, `HTTP.Responder`s. Vapor's `Droplet` and any route [group](group.md) created with Vapor are `RouteBuilder`s that accept HTTP responders.
```swift
let v1 = builder.grouped("v1")
```
From there you can create routes with the `builder` as usual. The `builder: B` will work exactly like a `Droplet` or route [group](group.md). Any methods that work there will work on this builder.
## Empty Initializable
You can even add `EmptyInitializable` to your route collection if it has an empty `init` method. This will allow you to add the route collection via its type name.
```swift
class V1Collection: RouteCollection, EmptyInitializable {
init() { }
...
```
Now we can add the collection without initializing it.
```swift
drop.collection(V1Collection.self)
```

View File

@ -1,74 +0,0 @@
---
currentMenu: routing-controller
---
# Introduction
Instead of defining all of your request handling logic as Closures in route files, you may wish to organize this behavior
using Controller classes. Controllers can group related request handling logic into a single class. Controllers are stored
in the `Sources/App/Controllers` directory.
# Basic Controller
## Defining Controllers
```swift
import Vapor
import HTTP
final class FirstController {
func index(request: Request) throws -> ResponseRepresentable {
return try JSON(node: [
"message": "This is FirstController's index method"
])
}
}
```
You can define a route to this controller action like so:
```swift
drop.get("getindex") {request in
return try FirstController().index(request: request)
}
```
Now, when a request matches the specified route URI, the Index method on the FirstController
class will be executed. Of course, the route parameters will also be passed to the method.
--------
# Resource Controllers
Vapor resource routing assigns the typical "CRUD" routes to a controller with a single line of code.
```swift
drop.resource("URI", Controller())
```
This single route declaration creates multiple routes to handle a variety of actions on the resource.
The generated controller will already have methods stubbed for each of these actions, including
notes informing you of the HTTP verbs and URIs they handle.
| Verb | URI | Action |
| :-------------: | :-------------: | :-----------: |
| GET | test/index | test.index |
| POST | test/create | test.create |
| GET | test/show | test.show |
| PUT | test/replace | test.replace |
| PATCH | test/destroy | test.destroy |
| DELETE | test/destroy | test.destroy |
| DELETE | test/clear | test.clear |
You can also custom method name, add `makeResource` method in the controller
```swift
func makeResource() -> Resource<First> {
return Resource(
index: index,
store: create,
show: show,
replace: replace,
modify: update,
destroy: delete,
clear: clear
)
}
```

View File

@ -1,67 +0,0 @@
---
currentMenu: routing-group
---
# Route Groups
Grouping routes together makes it easy to add common prefixes, middleware, or hosts to multiple routes.
Route groups have two different forms: Group and Grouped.
### Group
Group (without the "ed" at the end) takes a closure that is passed a `GroupBuilder`.
```swift
drop.group("v1") { v1 in
v1.get("users") { request in
// get the users
}
}
```
### Grouped
Grouped returns a `GroupBuilder` that you can pass around.
```swift
let v1 = drop.grouped("v1")
v1.get("users") { request in
// get the users
}
```
### Middleware
You can add middleware to a group of routes. This is especially useful for authentication.
```swift
drop.group(AuthMiddleware()) { authorized in
authorized.get("token") { request in
// has been authorized
}
}
```
### Host
You can limit the host for a group of routes.
```swift
drop.group(host: "vapor.codes") { vapor
vapor.get { request in
// only responds to requests to vapor.codes
}
}
```
### Chaining
Groups can be chained together.
```swift
drop.grouped(host: "vapor.codes").grouped(AuthMiddleware()).group("v1") { authedSecureV1 in
// add routes here
}
```

View File

@ -1,134 +0,0 @@
---
currentMenu: routing-parameters
---
# Routing Parameters
Traditional web frameworks leave room for error in routing by using strings for route parameter names and types. Vapor takes advantage of Swift's closures to provide a safer and more intuitive method for accessing route parameters.
## Type Safe
To create a type safe route simply replace one of the parts of your path with a `Type`.
```swift
drop.get("users", Int.self) { request, userId in
return "You requested User #\(userId)"
}
```
This creates a route that matches `users/:id` where the `:id` is an `Int`. Here's what it would look like using manual route parameters.
```swift
drop.get("users", ":id") { request in
guard let userId = request.parameters["id"]?.int else {
throw Abort.badRequest
}
return "You requested User #\(userId)"
}
```
Here you can see that type safe routing saves ~3 lines of code and also prevents runtime errors like misspelling `:id`.
## String Initializable
Any type that conforms to `StringInitializable` can be used as a type-safe routing parameter. By default, the following types conform:
- String
- Int
- Model
`String` is the most generic and always matches. `Int` only matches when the string supplied can be turned into an integer. `Model` only matches when the string, used as an identifier, can be used to find the model in the database.
Our previous example with users can be further simplified.
```swift
drop.get("users", User.self) { request, user in
return "You requested \(user.name)"
}
```
Here the identifier supplied is automatically used to lookup a user. For example, if `/users/5` is requested, the `User` model will be asked for a user with identifier `5`. If one is found, the request succeeds and the closure is called. If not, a not found error is thrown.
Here is what this would look like if model didn't conform to `StringInitializable`.
```swift
drop.get("users", Int.self) { request, userId in
guard let user = try User.find(userId) else {
throw Abort.notFound
}
return "You requested User #\(userId)"
}
```
Altogether, type safe routing can save around 6 lines of code from each route.
### Protocol
Conforming your own types to `StringInitializable` is easy.
```swift
public protocol StringInitializable {
init?(from string: String) throws
}
```
Here is what `Model`'s conformance looks like for those who are curious.
```swift
extension Model {
public init?(from string: String) throws {
if let model = try Self.find(string) {
self = model
} else {
return nil
}
}
}
```
The `init` method can both `throw` and return `nil`. This allows you to `throw` your own errors. Or, if you want the default error and behavior, just return `nil`.
### Limits
Type safe routing is currently limited to three path parts. This is usually remedied by adding route [groups](group.md).
```swift
drop.group("v1", "users") { users in
users.get(User.self, "posts", Post.self) { request, user, post in
return "Requested \(post.name) for \(user.name)"
}
}
```
The resulting path for the above example is `/v1/users/:userId/posts/:postId`. If you are clamoring for more type safe routing, please let us know and we can look into increasing the limit of three.
## Manual
As shown briefly above, you are still free to do traditional routing. This can be useful for especially complex situations.
```swift
drop.get("v1", "users", ":userId", "posts", ":postId", "comments", ":commentId") { request in
let userId = try request.parameters.extract("userId") as Int
let postId = try request.parameters.extract("postId") as Int
let commentId = try request.parameters.extract("commentId") as Int
return "You requested comment #\(commentId) for post #\(postId) for user #\(userId)"
}
```
> Property `request.parameters` is used to extract parameters encoded in the URI _path_ (for example, `/v1/users/1` has a parameter `:userId` equal to `"1"`). In case of parameters passed as a part of a _query_ (e.g. `/v1/search-user?userId=1`), the `request.data` should be used (e.g. `let userId = request.data["userId"]?.string`).
Request parameters can be accessed either as a dictionary or using the `extract` syntax which throws instead of returning an optional.
### Groups
Manual request parameters also work with [groups](group.md).
```swift
let userGroup = drop.grouped("users", ":userId")
userGroup.get("messages") { req in
let user = try req.parameters.extract("userId") as User
}
```

View File

@ -1,34 +0,0 @@
---
currentMenu: routing-query-parameters
---
# Query Parameters
Request query parameters can be accessed either as a dictionary or using the `extract` syntax which throws instead of returning an optional.
## Optional Syntax
Optional syntax is the easiest way to handle optional query parameters.
```swift
drop.get("comments") { request in
if let rating = request.query?["rating"]?.int {
return "You requested comments with rating greater than #\(rating)"
}
return "You requested all comments"
}
```
## Extract Syntax
Extract syntax might be useful to *enforce* the presence of query parameters and throw an exception if they are not present.
To use this syntax first we need to ensure the query object is present with a `guard`.
```swift
drop.get("comments") { request in
guard let rating = request.query?["rating"]?.int else {
throw Abort.custom(status: .preconditionFailed, message: "Please include a rating")
}
return "You requested comments with rating greater than #\(rating)"
}
```

View File

@ -1,3 +0,0 @@
# Redirecting...
<meta http-equiv="refresh" content="0; url=/1.5/">

View File

@ -1,3 +0,0 @@
# Redirecting...
<meta http-equiv="refresh" content="0; url=/2.0/">

View File

@ -1,3 +0,0 @@
# Redirecting...
<meta http-equiv="refresh" content="0; url=/3.0/">

View File

@ -1,3 +0,0 @@
# Redirecting...
<meta http-equiv="refresh" content="0; url=/4.0/">

View File

@ -1,199 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ title }}</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Code+Pro">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Quicksand:400,700,300">
<link rel="stylesheet" href="{{ baseUrl }}/styles/vapor-code.css">
<link rel="stylesheet" href="{{ baseUrl }}/styles/main.css">
</head>
<body>
<header>
<a class="logo" href="{{ baseUrl }}/">
<img src="{{ baseUrl }}/images/droplet.svg" alt="Vapor">
<h1>Vapor <em>Docs</em></h1>
</a>
<ul>
<li>
<a href="http://vapor.codes">Home</a>
</li>
<li>
<a href="http://example.vapor.codes">Example</a>
</li>
<li>
<a href="https://github.com/vapor/vapor">GitHub</a>
</li>
<li>
<a href="https://twitter.com/@codevapor">Twitter</a>
</li>
<li>
<a href="http://vapor.team">Slack</a>
</li>
</ul>
</header>
<nav>
<a href="#" class="toggle show">☰</a>
<a href="#" class="toggle close">&times;</a>
<div class="scroll">
{% for section in menu.sections %}
<section>
<h3>{{ section.name }}</h3>
<ul>
{% for itemId, item in section.items %}
<li class="{{ itemId == currentMenu ? 'active' }}">
<a href="{{ item.absoluteUrl|default(baseUrl ~ '/' ~ item.relativeUrl) }}">
{{ item.text | raw }}
</a>
</li>
{% endfor %}
</ul>
</section>
{% endfor %}
</div>
</nav>
<main>
<a href="https://github.com/vapor/documentation/blob/master/CONTRIBUTING.md" class="edit">✎ Edit on GitHub</a>
{{ content | raw }}
</main>
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="{{ baseUrl}}/scripts/highlight.pack.js"></script>
<script>
hljs.registerLanguage("leaf", function (hljs) {
/* Author: Hale Chan <halechan@qq.com> */
return {
c: [
{
cN: 'function',
b: '#+',
e: '[)] ',
rB: true,
eE: false,
c : [
{
b: '[(]',
e: '[)]',
eE: true,
c: [
{
cN: 'string',
b: '"',
e: '"'
},
{
cN: 'title',
b: '[A-Za-z_][A-Za-z_0-9]*'
}
]
},
{
cN: 'keyword',
b: '#+([A-Za-z$_][0-9A-Za-z$_]*)?',
eP: true
},
]
}
]
};
});
</script>
<script>
$(function() {
// Syntax highlighting
hljs.initHighlightingOnLoad();
if (navigator.userAgent.indexOf('Safari') != -1 && navigator.userAgent.indexOf('Chrome') == -1) {
$('body').addClass('safari');
}
$('h1, h2, h3, h4, h5, h6').each(function(key, item) {
var $item = $(item);
var id = $item.attr('id');
if (id) {
var link = $('<a>');
link.attr('href', '#' + id);
$item.wrap(link);
}
})
$('a.toggle').on('click', function(e){
e.preventDefault();
console.log('hi');
var body = $('body');
if (body.hasClass('toggled')) {
body.removeClass('toggled');
} else {
body.addClass('toggled');
}
})
var lastScroll = 0;
var isUp = false;
var win = $(window);
win.on('scroll', function(){
var scrollTop = win.scrollTop();
var scrollBottom = win.scrollTop() + win.height();
var docHeight = $(document).height();
if ( scrollTop < 0 || scrollBottom >= docHeight ){
return;
}
if (scrollTop > (lastScroll + 0) && scrollTop >= 0) {
if (!isUp) {
$('a.toggle.show').addClass('hide')
}
isUp = true;
lastScroll = scrollTop;
} else if (scrollTop < (lastScroll - 0)) {
if (isUp) {
$('a.toggle.show').removeClass('hide')
}
isUp = false;
lastScroll = scrollTop;
}
});
// scroll to the active nav item
$("nav .scroll").animate({
scrollTop: $("nav .active").offset().top - 157
}, 0);
// var lastScrollTop = 0;
// $(window).scroll(function(event){
// var st = $(this).scrollTop();
// if (st > lastScrollTop){
// $('a.toggle.show').addClass('hide')
// } else {
// $('a.toggle.show').removeClass('hide')
// }
// lastScrollTop = st;
// });
});
</script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-76177358-4', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>

View File

@ -1,9 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 -5 48 60" enable-background="new 0 0 48 48" xml:space="preserve">
<defs>
<linearGradient id="linear" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#F7CAC9"/>
<stop offset="100%" stop-color="#92A8D1"/>
</linearGradient>
</defs>
<path stroke="url(#linear)" fill="white" fill-opacity="0" stroke-width="3" d="M24.8,0.9c-0.4-0.5-1.2-0.5-1.6,0C19.8,5.3,7.1,22.5,7.1,30.6c0,9.3,7.6,16.9,16.9,16.9s16.9-7.6,16.9-16.9 C40.9,22.5,28.2,5.3,24.8,0.9z"/>
</svg>

Before

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,316 +0,0 @@
html, body {
margin: 0;
padding: 0;
font-size: 14px;
line-height: 1.5;
font-family: -webkit-system-font, sans-serif; }
@media screen and (min-width: 720px) {
html, body {
width: 100%;
height: 100%; } }
a {
color: #94a9d1;
text-decoration: none;
border-bottom: 1px dotted; }
a:hover {
color: #f6cfcf; }
h1, h2, h3, h4, h5, h6 {
color: #333;
margin: 0;
font-family: "Quicksand";
letter-spacing: -1px; }
h1 {
font-size: 38px;
font-weight: 300; }
@media screen and (min-width: 720px) {
h1 {
font-size: 42px; } }
h2 {
font-size: 30px;
font-weight: 400; }
h3 {
font-size: 24px;
font-weight: 400;
margin-bottom: 6px; }
h4 {
font-size: 18px;
font-weight: 400;
margin-bottom: 6px; }
:target {
background: #ffff99; }
a.logo {
display: block;
position: relative;
border-bottom: none;
height: 100%;
width: 280px;
margin: 0 auto;
padding-left: 52px; }
@media screen and (min-width: 720px) {
a.logo {
padding-left: 72px;
width: 100%; } }
a.logo h1 {
margin: 0;
font-size: 36px;
line-height: 96px;
text-transform: uppercase; }
@media screen and (min-width: 720px) {
a.logo h1 {
font-size: 36px; } }
a.logo h1 em {
font-style: normal;
color: rgba(0, 0, 0, 0.4);
text-transform: none; }
a.logo img {
left: 6px;
top: 24px;
position: absolute;
height: 48px; }
@media screen and (min-width: 720px) {
a.logo img {
left: 24px;
top: 24px;
height: 48px; } }
* {
box-sizing: border-box; }
header {
overflow: hidden;
border-bottom: 3px solid #94a9d1;
height: 132px;
position: relative; }
@media screen and (min-width: 720px) {
header {
height: 96px;
position: absolute;
left: 0;
right: 0;
z-index: 10;
background: rgba(255, 255, 255, 0.95); }
.safari header {
background: rgba(255, 255, 255, 0.5);
-webkit-backdrop-filter: blur(10px); } }
header ul {
position: absolute;
margin: 0;
padding: 0;
bottom: 5px;
right: 0;
text-align: center; }
@media screen and (max-width: 720px) {
header ul {
left: 0; } }
@media screen and (min-width: 720px) {
header ul {
text-align: right;
right: 24px;
top: 24px;
height: 100%; } }
header ul li {
display: inline-block;
line-height: 48px; }
header ul li a {
padding: 0 6px;
display: block;
border-bottom: none; }
a.toggle {
position: absolute; }
@media screen and (min-width: 720px) {
a.toggle {
display: none; } }
a.toggle.close {
top: 12px;
right: 0;
z-index: 20;
color: white;
font-size: 48px;
line-height: 48px;
height: 72px;
width: 72px;
padding: 12px;
border-bottom: none;
text-align: center; }
a.toggle.show {
text-align: center;
border-bottom: none;
background: #94a9d1;
color: white;
right: -36px;
top: 50%;
height: 48px;
width: 36px;
margin-top: -24px;
line-height: 48px;
border-bottom-right-radius: 5px;
border-top-right-radius: 5px;
z-index: 15;
transition: right 0.25s; }
a.toggle.show.hide {
right: -12px; }
body.toggled a.toggle.show {
right: -36px; }
nav {
font-size: 14px;
border-right: 1px solid rgba(0, 0, 0, 0.05);
width: 100%;
position: fixed;
left: -100%;
transition: left 0.25s;
top: 0;
right: 0;
bottom: 0;
display: block;
background: #94a9d1;
z-index: 10; }
nav a {
border-bottom: none;
color: rgba(255, 255, 255, 0.9);
font-size: 16px;
line-height: 32px;
height: 32px;
display: block; }
nav h3 {
text-transform: uppercase;
margin-top: 12px;
font-size: 18px;
color: white;
font-weight: 600; }
body.toggled nav {
left: 0; }
@media screen and (min-width: 720px) {
nav {
background: white;
display: block;
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 216px;
z-index: 8;
padding-top: 96px; }
nav h3 {
color: #bbb;
font-size: 14px;
margin-bottom: 6px;
font-weight: 400; }
nav a {
border-bottom: none;
font-weight: 200;
font-size: 14px;
color: #777;
font-size: 12px;
height: 24px; }
nav a:hover {
color: #94a9d1; } }
nav ul, nav ul li {
list-style: none;
padding: 0;
margin: 0; }
nav ul.active a, nav ul li.active a {
font-weight: 600; }
@media screen and (min-width: 720px) {
nav ul.active a, nav ul li.active a {
color: #94a9d1; } }
nav ul {
margin-bottom: 18px; }
div.scroll {
padding: 22px;
overflow-y: scroll;
height: 100%;
z-index: 10;
-webkit-overflow-scrolling: touch; }
main {
-webkit-overflow-scrolling: touch;
color: #555;
font-weight: 200;
width: 100%;
padding: 24px;
overflow: hidden; }
@media screen and (min-width: 720px) {
main {
height: 100%;
position: absolute;
position: relative;
z-index: 6;
top: 0;
bottom: 0;
right: 0;
left: 0;
padding: 22px;
padding-top: 110px;
padding-left: 240px;
overflow-y: scroll;
max-width: 1440px; } }
main a.edit {
position: absolute;
top: 146px;
right: 24px;
border-bottom: none; }
@media screen and (min-width: 720px) {
main a.edit {
top: 102px; } }
main h1 {
margin-bottom: 12px; }
main h2 {
margin-top: 24px;
margin-bottom: 12px;
border-bottom: 1px dotted rgba(0, 0, 0, 0.1); }
main h3 {
margin-top: 18px; }
main p {
margin-top: 0;
margin-bottom: 12px;
line-height: 1.75; }
main blockquote {
margin: 0;
background: rgba(247, 202, 201, 0.2);
margin-top: 18px;
margin-bottom: 18px;
margin-left: 0;
padding: 6px;
padding-left: 12px;
border-left: 5px solid #f6cfcf; }
main blockquote p {
margin: 0;
padding: 0; }
pre, code {
font-size: 12px;
font-family: "Source Code Pro", monospace;
-webkit-overflow-scrolling: touch; }
p code, li code {
background: #fbfbfb;
border-radius: 5px;
padding: 3px 5px;
display: inline-block;
color: #6b7891;
box-shadow: 0 1px 0px 0px rgba(0, 0, 0, 0.1); }
pre {
margin-top: 12px;
margin-bottom: 15px;
padding: 5px;
background: #fbfbfb;
border-radius: 10px;
box-shadow: 0 3px 0px 0px rgba(0, 0, 0, 0.1); }
img {
max-width: 100%; }
/*# sourceMappingURL=main.css.map */

File diff suppressed because one or more lines are too long

View File

@ -1,351 +0,0 @@
$tablet: 720px
$blue: #94a9d1
html, body
margin: 0
padding: 0
font-size: 14px
line-height: 1.5
font-family: -webkit-system-font, sans-serif
@media screen and (min-width: $tablet)
width: 100%
height: 100%
a
color: #94a9d1
text-decoration: none
border-bottom: 1px dotted
a:hover
color: #f6cfcf
h1, h2, h3, h4, h5, h6
color: #333
margin: 0
font-family: 'Quicksand'
letter-spacing: -1px
h1
font-size: 38px
@media screen and (min-width: $tablet)
font-size: 42px
font-weight: 300
h2
font-size: 30px
font-weight: 400
h3
font-size: 24px
font-weight: 400
margin-bottom: 6px
h4
font-size: 18px
font-weight: 400
margin-bottom: 6px
\:target
background: #ffff99
a.logo
display: block
position: relative
border-bottom: none
height: 100%
width: 280px
margin: 0 auto
padding-left: 52px
@media screen and (min-width: $tablet)
padding-left: 72px
width: 100%
h1
margin: 0
font-size: 36px
line-height: 96px
text-transform: uppercase
@media screen and (min-width: $tablet)
font-size: 36px
em
font-style: normal
color: rgba(0, 0, 0, 0.4)
text-transform: none
img
left: 6px
top: 24px
position: absolute
height: 48px
@media screen and (min-width: $tablet)
left: 24px
top: 24px
height: 48px
*
box-sizing: border-box
header
overflow: hidden
border-bottom: 3px solid #94a9d1
height: 132px
position: relative
@media screen and (min-width: $tablet)
height: 96px
position: absolute
left: 0
right: 0
z-index: 10
background: rgba(255, 255, 255, 0.95)
.safari &
background: rgba(255, 255, 255, 0.5)
-webkit-backdrop-filter: blur(10px)
ul
position: absolute
margin: 0
padding: 0
bottom: 5px
right: 0
text-align: center
@media screen and (max-width: $tablet)
left: 0
@media screen and (min-width: $tablet)
text-align: right
right: 24px
top: 24px
height: 100%
li
display: inline-block
line-height: 48px
a
padding: 0 6px
display: block
border-bottom: none
a.toggle
@media screen and (min-width: $tablet)
display: none
position: absolute
&.close
top: 12px
right: 0
z-index: 20
color: white
font-size: 48px
line-height: 48px
height: 72px
width: 72px
padding: 12px
border-bottom: none
text-align: center
&.show
text-align: center
border-bottom: none
background: $blue
color: white
right: -36px
top: 50%
height: 48px
width: 36px
margin-top: -24px
line-height: 48px
border-bottom-right-radius: 5px
border-top-right-radius: 5px
z-index: 15
transition: right 0.25s
&.hide
right: -12px
body.toggled &
right: -36px
nav
font-size: 14px
border-right: 1px solid rgba(0, 0, 0, 0.05)
width: 100%
position: fixed
left: -100%
transition: left 0.25s
top: 0
right: 0
bottom: 0
display: block
background: $blue
z-index: 10
a
border-bottom: none
color: rgba(255, 255, 255, 0.9)
font-size: 16px
line-height: 32px
height: 32px
display: block
h3
text-transform: uppercase
margin-top: 12px
font-size: 18px
color: white
font-weight: 600
body.toggled &
left: 0
@media screen and (min-width: $tablet)
background: white
display: block
position: absolute
left: 0
top: 0
bottom: 0
width: 216px
z-index: 8
padding-top: 96px
h3
color: #bbb
font-size: 14px
margin-bottom: 6px
font-weight: 400
a
border-bottom: none
font-weight: 200
font-size: 14px
color: #777
font-size: 12px
height: 24px
&:hover
color: #94a9d1
ul, ul li
list-style: none
padding: 0
margin: 0
&.active a
@media screen and (min-width: $tablet)
color: #94a9d1
font-weight: 600
ul
margin-bottom: 18px
div.scroll
padding: 22px
overflow-y: scroll
height: 100%
z-index: 10
-webkit-overflow-scrolling: touch
main
-webkit-overflow-scrolling: touch
color: #555
font-weight: 200
width: 100%
padding: 24px
overflow: hidden
@media screen and (min-width: $tablet)
height: 100%
position: absolute
position: relative
z-index: 6
top: 0
bottom: 0
right: 0
left: 0
padding: 22px
padding-top: 110px
padding-left: 240px
overflow-y: scroll
max-width: 1440px
a.edit
position: absolute
top: 146px
@media screen and (min-width: $tablet)
top: 102px
right: 24px
border-bottom: none
h1
margin-bottom: 12px
h2
margin-top: 24px
margin-bottom: 12px
border-bottom: 1px dotted rgba(0, 0, 0, 0.10)
h3
margin-top: 18px
p
margin-top: 0
margin-bottom: 12px
line-height: 1.75
blockquote
margin: 0
background: rgba(247, 202, 201, 0.2)
margin-top: 18px
margin-bottom: 18px
margin-left: 0
padding: 6px
padding-left: 12px
border-left: 5px solid #f6cfcf
p
margin: 0
padding: 0
pre, code
font-size: 12px
font-family: 'Source Code Pro', monospace
-webkit-overflow-scrolling: touch
p code, li code
background: #fbfbfb
border-radius: 5px
padding: 3px 5px
display: inline-block
color: #6b7891
box-shadow: 0 1px 0px 0px rgba(0, 0, 0, 0.1)
pre
margin-top: 12px
margin-bottom: 15px
padding: 5px
background: #fbfbfb
border-radius: 10px
box-shadow: 0 3px 0px 0px rgba(0, 0, 0, 0.1)
img
max-width: 100%

View File

@ -1,71 +0,0 @@
/**
* GitHub Gist Theme
* Author : Louis Barranqueiro - https://github.com/LouisBarranqueiro
*/
.hljs {
display: block;
padding: 0.5em;
color: #6b7891;
background: none;
overflow-x: auto;
}
.hljs-comment,
.hljs-meta {
color: #969896;
}
.hljs-string,
.hljs-variable,
.hljs-template-variable,
.hljs-strong,
.hljs-emphasis,
.hljs-quote {
color: #9cc074;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-type {
color: #9f69c4;
}
.hljs-literal,
.hljs-symbol,
.hljs-bullet,
.hljs-attribute {
color: #0086b3;
}
.hljs-section,
.hljs-name {
color: #63a35c;
}
.hljs-tag {
color: #333333;
}
.hljs-title,
.hljs-attr,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #795da3;
}
.hljs-addition {
color: #55a532;
background-color: #eaffea;
}
.hljs-deletion {
color: #bd2c00;
background-color: #ffecec;
}
.hljs-link {
text-decoration: underline;
}

View File

@ -1,105 +0,0 @@
---
currentMenu: testing-basic
---
# Basic Testing
Testing is a critical part of any software application, and Vapor apps should be no different. In this documentation, we'll cover some of the basic setup required to be able to test against our `Droplet`.
## Displacing Droplet Creation Logic
Up to this point, a lot of our documentation has centered around putting our `Droplet` creation logic in `main.swift`. Unfortunately, when testing against our application, this code becomes largely inaccessible. The first thing we'll need to do is break this out and move the running into a new `Run` module.
Here's an example of my setup file in `App`. I name mine `Droplet+Setup.swift`. Here's how it might look:
```swift
import Vapor
public func load(_ drop: Droplet) throws {
drop.preparations.append(Todo.self)
drop.get { _ in return "put my droplet's logic in this `load` function" }
drop.post("form") { req in
...
return Response(body: "Successfully posted form.")
}
// etc.
}
```
> [WARNING] Do **not** call `run()` anywhere within the `load` function as `run()` is a blocking call.
## Updated `main.swift`
Now that we've abstracted our loading logic, we'll need to move our `main.swift` into the `Run` module.
Next, we need to update `main.swift` it to reflect those changes. Here's how it should look after:
```swift
import Vapor
import App
let drop = Droplet(...)
try load(drop)
drop.run()
```
> The reason we still initialize `Droplet` outside of the scope of `load` is so that we can have the option to initialize differently for testing. We'll cover that soon.
## Testable Droplet
The first thing we'll do is in my testing target `AppTests`, add a file called `Droplet+Test.swift`. It will look like this:
```swift
@testable import Vapor
import App
func makeTestDroplet() throws -> Droplet {
let drop = Droplet(arguments: ["dummy/path/", "prepare"])
try load(drop)
try drop.runCommands()
return drop
}
```
This looks a lot like our initializer in `main.swift`, but there are 3 very key differences.
### Droplet(arguments: ["dummy/path/", "prepare"], ...
The `arguments:` parameter in our `Droplet` creation. This is rarely used except for advanced situations, but we'll use it here in testing to ensure that our `Droplet` doesn't try to automatically `serve` and block our thread. You can use arguments besides `"prepare"`, but unless you're doing something specific for an advanced situation, these arguments should suffice.
### try drop.runCommands()
You'll notice here that we're calling `runCommands()` instead of `run()`. This allows the `Droplet` to do all the setup it would normally do before booting without actually binding to a socket or exiting.
### `@testable import Vapor`
We'll need to import the testable compilation of Vapor to access the `runCommands` function. This is currently not public as a protection against accidental bugs in live apps.
## Test Our Droplet
Now that all of this has been created, we're ready to start testing our application's `Droplet` by adding tests under `AppLogicTests`. Here's how a really basic test might look:
```swift
import XCTest
import HTTP
@testable import App
class SmokeTest: XCTestCase {
func testEndpoint() throws {
let drop = try makeTestDroplet()
let request = try Request(method: .get, uri: "/")
let expectedBody = "It works."
let response = try drop.respond(to: request)
let responseBody = try response.body.bytes!.string()
XCTAssertTrue(responseBody.contains(expectedBody))
}
}
```
Notice that now you can use `CMD-U` to run your tests in Xcode with in-line results. In addition, you can run `vapor test` to test your code from the command line. If you choose to use `swift build` instead and you are using MySQL in your app, make sure you add the correct build flags to the call.
Good luck, and happy testing!

View File

@ -1,47 +0,0 @@
---
currentMenu: testing-modules
---
# Using Multiple Modules For Testing
Testing a Vapor app gets tricky, and requires some maneuvering of your app targets.
> [WARNING] Technically this is only necessary if you plan to run your tests on Linux. You can keep your tests in the same module if you want to only run your tests from the command line using `vapor test`
## **Step 1:** Update Package.swift
To start, you need to split up your Vapor project into a target called `App`, and a target called `Run`. The `Run` module will only include a `main.swift`, and your `App` will contain the actual logic for the app.
Add a `Sources/Run` folder to your project, then add `targets` to your `Package.swift`:
```swift
import PackageDescription
let package = Package(
name: “ProjectName”,
targets: [
Target(name: "App"),
Target(name: "Run", dependencies: ["App"])
],
dependencies: [
.Package(url: "https://github.com/vapor/vapor.git", majorVersion: 1, minor: 5)
],
exclude: [
"Config",
"Database",
"Localization",
"Public",
"Resources"
]
)
```
## **Step 2:** Update Tests Folder
If you don't already have a `Tests` folder at the root of your project, add one.
Make sure that your tests folder has a file called `LinuxMain.swift` and a folder called `AppTests`. In your `AppTests`, you can add your testing files like `UserTests.swift`.
As always, make sure that you regenerate with `vapor xcode -y`.
As long as there is at least one test file under `AppTests`, your generated Xcode project will have an `AppTests` target that you can run as usual. You can also run the tests from the command line with `vapor test`.

View File

@ -1,58 +0,0 @@
---
currentMenu: websockets-custom
---
# Custom WebSockets
Below are some examples of WebSockets using the underlying Engine package.
## Client
```Swift
import WebSockets
try WebSocket.connect(to: url) { ws in
print("Connected to \(url)")
ws.onText = { ws, text in
print("[event] - \(text)")
}
ws.onClose = { ws, _, _, _ in
print("\n[CLOSED]\n")
}
}
```
## Server
```Swift
import HTTP
import WebSockets
import Transport
final class MyResponder: Responder {
func respond(to request: Request) throws -> Response {
return try request.upgradeToWebSocket { ws in
print("[ws connected]")
ws.onText = { ws, text in
print("[ws text] \(text)")
try ws.send("🎙 \(text)")
}
ws.onClose = { _, code, reason, clean in
print("[ws close] \(clean ? "clean" : "dirty") \(code?.description ?? "") \(reason ?? "")")
}
}
}
}
let port = 8080
let server = try Server<TCPServerStream, Parser<Request>, Serializer<Response>>(port: port)
print("Connect websocket to http://localhost:\(port)/")
try server.start(responder: MyResponder()) { error in
print("Got server error: \(error)")
}
```

View File

@ -1,59 +0,0 @@
---
currentMenu: websockets-droplet
---
# Droplet WebSockets
Creating a WebSocket server with the Droplet is easy. WebSockets work by upgrading an HTTP request to a WebSocket connection.
Because of this, you should pick a URL for your WebSocket server to reside at. In this case, we use `/ws`.
```swift
import Vapor
let drop = Droplet()
drop.socket("ws") { req, ws in
print("New WebSocket connected: \(ws)")
// ping the socket to keep it open
try background {
while ws.state == .open {
try? ws.ping()
drop.console.wait(seconds: 10) // every 10 seconds
}
}
ws.onText = { ws, text in
print("Text received: \(text)")
// reverse the characters and send back
let rev = String(text.characters.reversed())
try ws.send(rev)
}
ws.onClose = { ws, code, reason, clean in
print("Closed.")
}
}
drop.run()
```
To connect with a WebSocket client, you would open a connection to `ws://<ip>/ws`.
Here is an example using JavaScript.
```swift
var ws = new WebSocket("ws://0.0.0.0:8080/ws")
ws.onmessage = function(msg) {
console.log(msg)
}
ws.onopen = function(event) {
ws.send("test")
}
```
The above will log `tset` (`test` reversed).

View File

@ -1,229 +0,0 @@
# Getting Started
Vapor's [Auth Provider](https://github.com/vapor/auth-provider) package makes implementing authentication and
authorization easy and secure. It supports common auth patterns such as:
- Token (bearer) authentication
- Username + password (basic) authentication
- Permission-based authorization
- Session-based persistance
Auth's modular, protocol-based nature also makes it a great foundation for custom auth needs.
!!! tip
Use `vapor new <name> --template=vapor/auth-template` to create a new [project template](https://github.com/vapor/auth-template) with AuthProvider and samples included.
## Package
To use Auth, you will need to have the [Auth Provider](https://github.com/vapor/auth-provider) added to your project.
This is as simple as adding the following line to your `Package.swift` file.
```swift
.Package(url: "https://github.com/vapor/auth-provider.git", ...)
```
Check out the [Package](package.md) section for more information.
## Example
Let's take a look at how we can implement a simple, token-based authentication system using Vapor and Auth.
### User
We will start by creating a model to represent our user. If you already have a user class, you can skip this step.
```swift
import Vapor
import FluentProvider
final class ExampleUser: Model {
let name: String
...
}
extension ExampleUser: Preparation { ... }
```
Here we create a very simple user with just one property: a name.
!!! seealso
We're omitting most of `Model` and `Preparation` protocol requirements. Check out Fluent's
[Getting Started](../fluent/getting-started.md) for more information about these protocols.
### Token
Next let's create a model to represent our authentication tokens. These will be stored in a separate database
table or collection called "tokens".
When a user logs in, we will create a new token for them. They will then use this token on subsequent requests
instead of their username and password.
For now, here's what our simple token model will look like.
```swift
import Vapor
import FluentProvider
final class ExampleToken: Model {
let token: String
let userId: Identifier
var user: Parent<ExampleToken, ExampleUser> {
return parent(id: userId)
}
...
}
extension ExampleToken: Preparation { ... }
```
This token has two properties:
- token: a unique, random string that we will send in requests
- userId: the identifier for the user to whom this token belongs
!!! seealso
We're using Fluent relations here. Check out Fluent's [Relations](../fluent/relations.md)
section for more information.
### Token Authenticatable
Now that we have our example user and token, we can make our user authenticatable with the token.
This might sound complicated, but it's actually pretty easy:
```swift
import AuthProvider
extension ExampleUser: TokenAuthenticatable {
// the token model that should be queried
// to authenticate this user
public typealias TokenType = ExampleToken
}
```
Now that our example user is `TokenAuthenticatable`, we can move on to the next step!
### User Helper
Let's add a simple convenience method on request for accessing the authenticated user.
```swift
extension Request {
func user() throws -> ExampleUser {
return try auth.assertAuthenticated()
}
}
```
This is a nice shortcut that will come in handy in a few steps.
### Middleware
To require authentication we need to add the `TokenAuthenticationMiddleware`. You can apply this middleware
to individual routes or to the entire Droplet. For simplicity, we'll apply it to the Droplet.
```swift
import Vapor
import AuthProvider
import FluentProvider
let config = try Config()
config.preparations.append(ExampleUser.self)
config.preparations.append(ExampleToken.self)
let drop = try Droplet(config)
let tokenMiddleware = TokenAuthenticationMiddleware(ExampleUser.self)
/// use this route group for protected routes
let authed = drop.grouped(tokenMiddleware)
```
Since our `ExampleUser` class is `TokenAuthenticatable`, we can pass it into the middleware's init method.
!!! seealso
If you only want to require authentication for certain routes, look at our
[Route Group](../routing/group.md) section in the routing docs.
### Route
Now that we have a route group protected by our TokenMiddleware, let's add a route to
return the authenticated user's name.
```swift
authed.get("me") { req in
// return the authenticated user's name
return try req.user().name
}
```
!!! tip
We're using the `.user()` convenience we added to `Request` here. It is a shortcut
for `let user = try req.auth.assertAuthenticated(ExampleUser.self)`
### Database
That's it! We now have a functioning authentication system. Let's add a couple of entries
to our database and test it out.
#### Users
| id | name |
|----|------|
| 1 | Bob |
#### Tokens
| id | token | user_id |
|----|-------|---------|
| 1 | foo | 1 |
### Request
Now we can make a request to our Vapor app.
```http
GET /me HTTP/1.1
Authorization: Bearer foo
```
And we should get a response like.
```http
HTTP/1.1 200 OK
Content-Type: text/plain
Bob
```
#### Bad Token
To make sure it's secure, let's test using a token that's not in our database.
```http
GET /me HTTP/1.1
Authorization: Bearer not-a-token
```
And we should get a response like.
```http
HTTP/1.1 403 Forbidden
```
### Next Steps
To build this out into a production-ready authentication system, you will need to build some
additional routes for creating users and creating tokens.
Continue on in the Auth section to learn more about different types of authentication.

View File

@ -1,81 +0,0 @@
# Auth Helper
The Auth package adds a convenience property on every request that makes it
easy to authenticate, persist, and unauthenticate users.
## Authentication
### Checking
You can get the currently authenticated user.
```swift
let user = req.auth.authenticated(User.self)
```
You can check to see if the user is authenticated.
```swift
if req.auth.isAuthenticated(User.self) {
...
}
```
You can also assert that the user is authenticated.
```swift
let user = try req.auth.assertAuthenticated(User.self)
```
!!! note:
A 403 Forbidden error will be thrown if the user is not authenticated.
### Manual
You can manually authenticate a user.
```swift
if let user = try User.find(1) {
req.auth.authenticate(user)
}
```
You can also unauthenticate the currently authenticated user.
```swift
try req.auth.unauthenticate()
```
!!! note:
If the user is `Persistable`, they will also be unpersisted.
## Headers
The helper can be used to access common authorization headers.
```swift
print(req.auth.header)
```
### Token
The header has additional conveniences for parsing out bearer tokens.
```swift
print(req.auth.header?.bearer)
```
!!! tip
You can use `_authorizationBasic` and `_authorizationBearer` to send tokens in the URL string.
### Password
And basic auth username + password.
```swift
print(req.auth.header?.basic)
```

View File

@ -1,55 +0,0 @@
# Using Auth
This section outlines how to import the Auth package both with or without a Vapor project.
## With Vapor
The easiest way to use Auth with Vapor is to include the Auth provider.
You can achieve this by running:
```bash
vapor provider add auth
```
or by manually modifying your `Package.swift` file:
```swift
import PackageDescription
let package = Package(
name: "Project",
dependencies: [
.Package(url: "https://github.com/vapor/vapor.git", majorVersion: 2),
.Package(url: "https://github.com/vapor/auth-provider.git", majorVersion: 1)
],
exclude: [ ... ]
)
```
The Auth provider package adds Auth to your project and adds some additional, vapor-specific conveniences like auth middleware.
After you added the dependency, fetch it using `vapor update`.
Using `import AuthProvider` will import all of the auth middleware and the Authentication and Authorization modules.
## Without Vapor
At the core of the Vapor Auth provider is an Authentication and Authorization module based on Fluent, which you can use as a stand-alone package by including the Auth in your `Package.swift` files:
```swift
import PackageDescription
let package = Package(
name: "Project",
dependencies: [
...
.Package(url: "https://github.com/vapor/auth.git", majorVersion: 1)
],
exclude: [ ... ]
)
```
After you added the dependency, fetch it using `vapor update`.
Use `import Auth` to access the core auth classes.

View File

@ -1,125 +0,0 @@
# Username + Password (Basic) Auth
The `Authorization: Basic ...` header can be used to send username and password credentials
for authentication.
This page will show you how to use this type of authentication in your web app.
!!! note:
Sending and storing passwords should be avoided wherever possible. Use tokens or
sessions persistance to prevent the need for sending the password in every request.
## Password Authenticatable
Start by conforming your user model to the `PasswordAuthenticatable` protocol.
```swift
import AuthProvider
extension User: PasswordAuthenticatable { }
```
### Custom
If your user conforms to `Model`, all of the required methods will be implemented automatically. However,
you can implement them if you want to do something custom.
```swift
extension User: PasswordAuthenticatable {
/// Return the user matching the supplied
/// username and password
static func authenticate(_: Password) throws -> Self {
// something custom
}
/// The entity's hashed password used for
/// validating against Password credentials
/// with a PasswordVerifier
var hashedPassword: String? {
// something custom
}
/// The key under which the user's username,
/// email, or other identifing value is stored.
static var usernameKey: String {
// something custom
}
/// The key under which the user's password
/// is stored.
static var passwordKey: String {
// something custom
}
/// Optional password verifier to use when
/// comparing plaintext passwords from the
/// Authorization header to hashed passwords
/// in the database.
static var passwordVerifier: PasswordVerifier? {
// some hasher
}
}
```
## Middleware
Once your model conforms to the `PasswordAuthenticatable` protocol, you can create the middleware.
```swift
import Vapor
import AuthProvider
let drop = try Droplet()
let passwordMiddleware = PasswordAuthenticationMiddleware(User.self)
let authed = try drop.grouped(passwordMiddleware)
try drop.run()
```
All routes added to the `authed` route group will be protected by the password middleware.
!!! seealso
If you only want to globally require the password middleware, checkout the
[Middleware Config](../http/middleware.md/#config) section in the HTTP docs.
### Route
Now you can add a route to return the authenticated user.
```swift
authed.get("me") { req in
// return the authenticated user
return try req.auth.assertAuthenticated(User.self)
}
```
Call `req.user.authenticated(User.self)` to get access to the authenticated user.
## Request
Now we can make a request to our Vapor app.
```http
GET /me HTTP/1.1
Authorization: Basic dmFwb3I6Zm9v
```
!!! note
`dmFwb3I6Zm9v` is "vapor:foo" base64 encoded where "vapor" is the username and
"foo" is the password. This is the format of Basic authorization headers.
And we should get a response like.
```http
HTTP/1.1 200 OK
Content-Type: text/plain
Vapor
```

View File

@ -1,140 +0,0 @@
# Persisting Auth
Persisting authentication means that a user does not need to provide their credentials with every request.
This is useful for web apps where a user should only have to log in once.
!!! note
For APIs, it's recommended that the user send a token with every request.
See [Getting Started](getting-started.md) for an example about Token auth.
## Sessions
Sessions are built into Vapor by default and are an easy way to persist users in your web app.
### SessionPersistable
The first step is to conform your user model to the `SessionPersistable` protocol.
```swift
import AuthProvider
extension User: SessionPersistable {}
```
If your user is a Model, the protocol methods will be implemented automatically. However,
you can implement them if you want to do something custom.
```swift
import AuthProvider
import HTTP
extension User: SessionPersistable {
func persist(for: Request) throws {
// something custom
}
static func fetchPersisted(for: Request) throws -> Self? {
// something custom
}
}
```
### Middleware
Now that the user is `SessionPersistable`, we can create our middleware.
#### Sessions
First let's start by creating `SessionsMiddleware`. We'll use the `MemorySessions()` to get started.
```swift
let memory = MemorySessions()
let sessionsMiddleware = SessionsMiddleware(memory)
```
#### Persist
Now let's create the `PersistMiddleware`. This will take care of persisting our user once they've
been authenticated.
```swift
let persistMiddleware = PersistMiddleware(User.self)
```
Since our user conforms to `SessionPersistable` (and thus `Persistable`), we can pass it
into this middleware's init.
#### Authentication
Now to create the authentication middleware of your choice. We'll use `PasswordAuthenticationMiddleware`
which requires an `Authorization: Basic ...` header with the user's username and password.
```swift
let passwordMiddleware = PasswordAuthenticationMiddleware(User.self)
```
!!! note:
`User` must conform to `PasswordAuthenticatable` to be used with this middleware.
See the [Password](password.md) section to learn more.
### Droplet
Now we can create a Droplet and add all of our middleware.
```swift
import Vapor
import Sessions
import AuthProvider
let drop = try Droplet()
let authed = drop.grouped([sessionsMiddleware, persistMiddleware, passwordMiddleware])
```
!!! seealso
If you only want to globally require the password middleware, checkout the
[Middleware Config](../http/middleware.md/#config) section in the HTTP docs.
### Route
Now you can add a route to return the authenticated user.
```swift
authed.get("me") { req in
// return the authenticated user
return try req.auth.assertAuthenticated(User.self)
}
```
### Request
Now we can make a request to our Vapor app.
```http
GET /me HTTP/1.1
Authorization: Basic dmFwb3I6Zm9v
```
!!! note
`dmFwb3I6Zm9v` is "vapor:foo" base64 encoded where "vapor" is the username and
"foo" is the password. This is the format of Basic authorization headers.
And we should get a response like.
```http
HTTP/1.1 200 OK
Content-Type: text/plain
Set-Cookie: vapor-session=...
Vapor
```
Notice the `vapor-session` in the response. This can be used in subsequent requests instead of
the username and password.

View File

@ -1,23 +0,0 @@
# Auth Provider
After you've [added the Auth Provider package](package.md) to your project, setting the provider up in code is easy.
## Add to Droplet
Register the `AuthProvider.Provider` with your Droplet in your `Config+Setup.swift` file:
```swift
import App
import AuthProvider
let config = try Config()
try config.addProvider(AuthProvider.Provider.self)
let drop = try Droplet(config)
...
```
## Done
You are now ready to start using the Auth package.

View File

@ -1,121 +0,0 @@
# Redirect Middlewares
Included in the [AuthProvider](package.md) package are `RedirectMiddleware` and `InverseRedirectMiddleware` classes that will help you
redirect unauthenticated or authenticated requests to a given path. This is especially useful for redirecting users away from secure
pages to a login page and vice versa.
## Redirect Middleware
Let's take a look at how to add a `RedirectMiddleware` to your application.
### Existing Auth
Since we only want this middleware to apply to secure pages, we'll apply it using route groups.
You should already have a protected area in your application using one of the authentication middlewares.
```swift
import Vapor
import AuthProvider
let drop = try Droplet()
drop.get("login") { req in
return // some login form
}
let auth = TokenAuthenticationMiddleware(User.self)
let protected = drop.grouped([auth])
protected.get("secure") { req in
let user = try req.auth.assertAuthenticated(User.self)
return "Welcome to the secure page, \(user.name)"
}
```
The above snippet protects access to the page at `GET /secure` using the `TokenAuthenticationMiddleware`.
Since we've applied `TokenAuthenticationMiddleware`, this page cannot be accessed by anyone not authenticated.
Although this is perfectly secure, we should provide a better experience for unauthenticated users. Instead of
just showing them an error message, we can redirect them to the login page.
### Add Redirect
Creating a redirect middleware is very simple. We'll use one of the presets for redirecting a user to `/login`.
```swift
let redirect = RedirectMiddleware.login()
```
Now we just need to add this redirect middleware to our `protected` route group mentioned previously.
```swift
let protected = drop.grouped([redirect, auth])
```
!!! warning
Make sure the redirect middleware comes _before_ the auth middleware.
### Complete Example
Now whenever an unauthenticated user attemps to visit `GET /secure`, they will be redirected to `GET /login`.
```swift
import Vapor
import AuthProvider
let drop = try Droplet()
let redirect = RedirectMiddleware.login()
let auth = TokenAuthenticationMiddleware(TestUser.self)
let protected = drop.grouped([redirect, auth])
protected.get { req in
let user = try req.auth.assertAuthenticated(TestUser.self)
return "Welcome to the dashboard, \(user.name)"
}
```
### Custom Route
If your login page is not `/login` or you'd like the redirect middleware to redirect to a different type of page,
simply use the full initializer.
```swift
let redirect = RedirectMiddleware(path: "/foo")
```
## Inverse Redirect Middleware
Complementary to the `RedirectMiddleware` is the `InverseRedirectMiddleware`. Just like you want to redirect unauthenticated
users away from secure pages, you also might want to redirect _authenticated_ users away from certain pages.
For example, if a user is already authenticated and they visit the login page, they might be confused and attempt to login again.
### Example
Here is an example of the `InverseRedirectMiddleware` being used to redirect authenticated `User`s away from the login page.
We are using the preset `.home()` convenience, which redirects the user to `GET /`.
```swift
import Vapor
import AuthProvider
let drop = try Droplet()
let redirect = InverseRedirectMiddleware.home(User.self)
let group = drop.grouped([redirect])
group.get("login") { req in
return "Please login"
}
```
### Custom Route
If your desired page is not `/` or you'd like the inverse redirect middleware to redirect to a different type of page,
simply use the full initializer.
```swift
let redirect = InverseRedirectMiddleware(User.self, path: "/foo")
```

View File

@ -1,49 +0,0 @@
# Bits
The bits package is included in Vapor by default and provides a convenient API for working with bytes.
## Typealias
The bits package provides two type-aliases for bytes.
```swift
typealias Byte = UInt8
typealias Bytes = [Byte]
```
## BytesConvertible
It's quite often that we want to convert objects to and from byte arrays when we're working. The BytesConvertible helps define objects that have these capabilities. This is implemented already on most objects in Vapor that can/should be converted to and from byte arrays.
```Swift
let hello = String(bytes: [72, 101, 108, 108, 111])
let bytes = hello.makeBytes()
```
## String
Converting from bytes to string using the UTF-8 encoding is easy.
```swift
let bytes = "hello".makeBytes()
let string = bytes.makeString()
print(string) // "hello"
```
## Byte
The upper and lowercase latin alphabet and some additional control characters are statically typed on the `Byte`.
```swift
let bytes: Bytes = [.h, .e, .l, .l, .o]
print(bytes.makeString()) // "hello"
```
This makes byte manipulation and comparison easy and is useful for building things like parsers and serializers.
```swift
let byte: Byte = 65
if byte == .A {
print("found A!")
}
```

View File

@ -1,28 +0,0 @@
# Using Bits
## With Vapor
This package is included with Vapor by default, just add:
```Swift
import Bits
```
## Without Vapor
Bits provides a lot of byte-manipulation conveniences for any server-side Swift project. To include it in your package, add the following to your `Package.swift` file.
```swift
import PackageDescription
let package = Package(
name: "Project",
dependencies: [
...
.Package(url: "https://github.com/vapor/bits.git", majorVersion: 1)
],
exclude: [ ... ]
)
```
Use `import Bits` to access Bits' APIs.

View File

@ -1,78 +0,0 @@
# Cache
Vapor's `CacheProtocol` allows you to store and fetch items from a cache using optional expiration dates.
By default, the Droplet's cache is set to `MemoryCache`. See the various [providers](#providers) below.
## Store
Storing data into the cache is straightforward.
```swift
try drop.cache.set("hello", "world")
```
### Expiration
When storing data, you can also supply an expiration date.
```swift
try drop.cache.set("ephemeral", 42, expiration: Date(timeIntervalSinceNow: 30))
```
In the above example, the supplied key value pair will expire after 30 seconds.
## Fetch
You can retreive data from the cache using the `.get()` method.
```swift
try drop.cache.get("hello") // "world"
```
## Delete
Keys can be deleted from the cache using the `.delete()` method.
```swift
try drop.cache.delete("hello")
```
## Providers
Here is a list of official cache providers. You can [search GitHub](https://github.com/search?utf8=✓&q=topic%3Avapor-provider+topic%3Acache&type=Repositories) for additional packages.
| Type | Key | Description | Package | Class |
|--------|--------|---------------------------------|-----------------------------------------|-------------|
| Memory | memory | In-memory cache. Not persisted. | Vapor | MemoryCache |
| Fluent | fluent | Uses Fluent database. | [Fluent Provider](../fluent/package.md) | FluentCache |
| Redis | redis | Uses Redis database. | [RedisProvider](../redis/package.md) | RedisCache |
### How to Use
To use a different cache provider besides the default `MemoryCache`, make sure you have added the provider to your Package.
```swift
import Vapor
import <package>Provider
let config = try Config()
try config.addProvider(<package>Provider.Provider.self)
let drop = try Droplet(config)
...
```
Then change the Droplet's configuration file.
`Config/droplet.json`
```json
{
"cache": "<key>"
}
```

View File

@ -1,7 +0,0 @@
# Using Cache
This package is included with the Vapor dependency, use
```Swift
import Cache
```

View File

@ -1,221 +0,0 @@
# Config
An application's configuration settings. Cloud applications generally require complex configurations that can adjust based on their environment. Vapor intends to provide a flexible configuration interaction that can be customized for a given user.
## QuickStart
For Vapor applications, configuration files are expected to be nested under a top level folder named `Config`. Here's an example of a basic config featuring a single `servers` configuration.
```bash
./
├── Config/
│ ├── server.json
```
And an example of how this might look:
```JSON
{
"host": "0.0.0.0",
"port": 8080,
"securityLayer": "none"
}
```
What that's saying, is that our application should start a server on port `8080` and host `0.0.0.0`. This represents the following url: `http://localhost:8080`.
### Custom Keys
Let's add a custom key to the `server.json` file:
```JSON
{
"host": "0.0.0.0",
"port": 8080,
"securityLayer": "none",
"custom-key": "custom value"
}
```
This can be accessed from your application's config using the following.
```swift
let customValue = drop.config["server", "custom-key"]?.string ?? "default"
```
That's it, feel free to add and utilize keys as necessary to make your application configuration easier.
## Config Syntax
You can access your config directory with the following syntax. `app.config[fileName, path, to, key]`. For example, let's hypothesize that in addition to the `server.json` file we mentioned earlier, there is also a `keys.json` that looks like this:
```JSON
{
"test-names": [
"joe",
"jane",
"sara"
],
"mongo": {
"url" : "www.customMongoUrl.com"
}
}
```
We can access this file by making sure the first argument in our subscript is keys. To get the first name in our list:
```swift
let name = drop.config["keys", "test-names", 0]?.string ?? "default"
```
Or our mongo url:
```swift
let mongoUrl = drop.config["keys", "mongo", "url"]?.string ?? "default"
```
## Advanced Configurations
Having the default `server.json` is great, but what about more complex scenarios. For example, what if we want a different host in production and in development? These complex scenarios can be achieved by adding additional folders to our `Config/` directory. Here's an example of a folder structure that's setup for production and development environments.
```bash
WorkingDirectory/
├── Config/
│ ├── server.json
│ ├── production/
│ │ └── server.json
│ ├── development/
│ │ └── server.json
│ └── secrets/
│ └── server.json
```
> You can specify the environment through the command line by using --env=. Custom environments are also available, a few are provided by default: production, development, and testing.
```bash
vapor run --env=production
```
### PRIORITY
Config files will be accessed in the following priority.
1. CLI (see below)
2. Config/secrets/
3. Config/name-of-environment/
4. Config/
What this means is that if a user calls `app.config["server", "host"]`, the key will be searched in the CLI first, then the `secrets/` directory, then the top level default configs.
> `secrets/` directory should very likely be added to the gitignore.
### EXAMPLE
Let's start with the following JSON files.
#### `server.json`
```JSON
{
"host": "0.0.0.0",
"port": 9000
}
```
#### `production/server.json`
```JSON
{
"host": "127.0.0.1",
"port": "$PORT"
}
```
> The `"$NAME"` syntax is available for all values to access environment variables.
Please notice that `server.json`, and `production/server.json` both declare the same keys: `host`, and `port`. In our application, we'll call:
```swift
// will load 0.0.0.0 or 127.0.0.1 based on above config
let host = drop.config["server", "host"]?.string ?? "0.0.0.0"
// will load 9000, or environment variable port.
let port = drop.config["server", "port"]?.int ?? 9000
```
## Configuration file Options
#### `droplet.json`
```JSON
{
"server": "engine",
"client": "engine",
"console": "terminal",
"log": "console",
"hash": "crypto",
"cipher": "crypto",
"middleware": [
"error",
"date",
"file"
],
"commands": [
"prepare"
]
}
```
#### `server.json`
```JSON
{
"port": "$PORT:8080",
"host": "0.0.0.0",
"securityLayer": "none"
}
```
#### `fluent.json`
```JSON
{
"driver": "memory",
"keyNamingConvention": "snake_case",
"migrationEntityName": "fluent",
"pivotNameConnector": "_",
"autoForeignKeys": true,
"defaultPageKey": "page",
"defaultPageSize": 10,
"log": false,
"maxConnections":10
}
```
#### `crypto.json`
```JSON
{
"hash": {
"method": "sha256",
"encoding": "hex",
"key": "0000000000000000"
},
"cipher": {
"method": "aes256",
"encoding": "base64",
"key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
}
}
```
## COMMAND LINE
In addition to json files nested within the `Config/` directory, we can also use the command line to pass arguments into our config. By default, these values will be set as the "cli" file, but more complex options are also available.
If you want command line arguments set to a file besides "cli", you can use this more advanced specification. For example, the following CLI command:
```bash
--config:keys.analytics=124ZH61F
```
would be accessible within your application by using the following:
```swift
let analyticsKey = drop.config["keys", "analytics"]?.string
```

View File

@ -1,43 +0,0 @@
# Core
Core provides some conveniences for common tasks.
## Background
Easily create a background thread using `background()`
```swift
print("hello")
try background {
print("world")
}
```
## Portal
Portals allow you to make async tasks blocking.
```swift
let result = try Portal.open { portal in
someAsyncTask { result in
portal.close(with: result)
}
}
print(result) // the result from the async task
```
## RFC1123
Create RFC1123 type dates.
```swift
let now = Date().rfc1123 // string
```
You can also parse RFC1123 strings.
```
let parsed = Date(rfc1123: "Mon, 10 Apr 2017 11:26:13 GMT")
```

View File

@ -1,28 +0,0 @@
# Using Core
## With Vapor
This package is included with Vapor by default, just add:
```Swift
import Core
```
## Without Vapor
Core provides a lot of conveniences for any server-side Swift project. To include it in your package, add the following to your `Package.swift` file.
```swift
import PackageDescription
let package = Package(
name: "Project",
dependencies: [
...
.Package(url: "https://github.com/vapor/core.git", majorVersion: 2)
],
exclude: [ ... ]
)
```
Use `import Core` to access Core's APIs.

View File

@ -1,33 +0,0 @@
# Debugging
Conforming your error types to `Debuggable` allows Vapor to create richer error messages and makes debugging easier.
```swift
import Debugging
extension FooError: Debuggable {
// conform here
}
```
Now when a `FooError` is thrown, you will get a nice message in your console.
```sh
Foo Error: You do not have a `foo`.
Identifier: DebuggingTests.FooError.noFoo
Here are some possible causes:
- You did not set the flongwaffle.
- The session ended before a `Foo` could be made.
- The universe conspires against us all.
- Computers are hard.
These suggestions could address the issue:
- You really want to use a `Bar` here.
- Take up the guitar and move to the beach.
Vapor's documentation talks about this:
- http://documentation.com/Foo
- http://documentation.com/foo/noFoo
```

View File

@ -1,28 +0,0 @@
# Using Debugging
## With Vapor
This package is included with Vapor by default, just add:
```Swift
import Debugging
```
## Without Vapor
Debugging is a convenient protocol for providing more information about error messages. You can use it in any of your Swift projects.
```swift
import PackageDescription
let package = Package(
name: "Project",
dependencies: [
...
.Package(url: "https://github.com/vapor/debugging.git", majorVersion: 1)
],
exclude: [ ... ]
)
```
Use `import Debugging` to access Debugging' APIs.

View File

@ -1,130 +0,0 @@
# Deploying with Apache2
[Apache2](https://httpd.apache.org/) is an effort to develop and maintain an open-source HTTP server for modern operating systems including UNIX and Windows. While Vapor supports directly serving HTTP requests with or without TLS, proxying behind Apache2 can provide increased performance, security, and ease-of-use.
!!! note
This page is for proxying behind Apache2. The recommened method is proxying Vapor HTTP servers behind [Nginx](nginx.md).
## Overview
What does it mean to proxy an HTTP server? In short, a proxy acts as a middleman between the public internet and your HTTP server. Requests come to the proxy and then it sends them to Vapor.
An important feature of this middleman proxy is that it can alter or even redirect the requests. For instance, the proxy can require that the client use TLS (https), rate limit requests, or even serve public files without talking to your Vapor application.
![apache2-proxy](https://user-images.githubusercontent.com/2223276/28477961-5e32bafc-6e24-11e7-94f1-a09c59673d1f.png)
### More Detail
The default port for receiving HTTP requests is port `80` (and `443` for HTTPS). When you bind a Vapor server to port `80`, it will directly receive and respond to the HTTP requests that come to your server. When adding a proxy like Apache2, you bind Vapor to an internal port, like port `8080`.
!!! note
Ports greater than 1024 do not require `sudo` to bind.
When Vapor is bound to a port besides `80` or `443`, it will not be accessible to the outside internet. You then bind Apache2 to port `80` and configure it to route requests to your Vapor server bound at port `8080` (or whichever port you've chosen).
And that's it. If Apache2 is properly configured, you will see your Vapor app responding to requests on port `80`. Apache2 proxies the requests and responses invisibly.
## Install Apache2
The first step is installing Apache2. One of the great parts of Apache2 is the tremendous amount of community resources and documentation surrounding it. Because of this, we will not go into great detail here about installing Apache2 as there is almost definitely a tutorial for your specific platform, OS, and provider.
Tutorials:
- [How To Set Up Apache Virtual Hosts on Ubuntu 14.04 LTS](https://www.digitalocean.com/community/tutorials/how-to-set-up-apache-virtual-hosts-on-ubuntu-14-04-lts)
- [How To Set Up Apache Virtual Hosts on Ubuntu 16.04](https://www.digitalocean.com/community/tutorials/how-to-set-up-apache-virtual-hosts-on-ubuntu-16-04)
### APT
Apache2 can be installed through APT.
```sh
sudo apt-get update
sudo apt-get install apache2
```
Check whether Apache2 was installed correctly by visiting your server's IP address in a browser
```sh
http://server_domain_name_or_IP
```
### Service
The service can be started or stopped.
```sh
sudo service apache2 stop
sudo service apache2 start
sudo service apache2 restart
```
## Booting Vapor
Apache2 can be started an stopped with the `sudo service apache2 ...` commands. You will need something similar to start and stop your Vapor server.
There are many ways to do this, and they depend on which platform you are deploying to. Check out the [Supervisor](supervisor.md) and [Nginx](nginx.md) instructions to add commands for starting and stopping your Vapor app.
## Configure Proxy
The configuration files for enabled sites can be found in `/etc/apache2/sites-enabled/`.
Create a new file or copy the example template from `/etc/apache2/sites-available/` to get started.
Here is an example configuration file for a Vapor project called `Hello` in the home directory.
```apache
# example.com Configuration
<VirtualHost *:80>
DocumentRoot /home/vapor/Hello/Public/
ServerName hello.com
# Using ProxyPass will send the following headers:
# X-Forwarded-For: The IP address of the client.
# X-Forwarded-Host: The original host requested by the client in the Host HTTP request header.
# X-Forwarded-Server The hostname of the proxy server.
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
ProxyTimeout 3
</VirtualHost>
```
This configuration file assumes the `Hello` project binds to port `8080` when started in production mode.
### Serving Files
Apache2 can also serve public files without asking your Vapor app. This can improve performance by freeing up the Vapor process for other tasks under heavy load.
```apache
<VirtualHost *:80>
...
ProxyPreserveHost On
# Serve all files in Public folder directly, bypassing proxy (this must be before ProxyPass /)
ProxyPass /Public !
ProxyPass / http://127.0.0.1:8080/
...
</VirtualHost>
```
### TLS
Adding TLS is relatively straightforward as long as the certificates have been properly generated. To generate TLS certificates for free, check out [Let's Encrypt](https://letsencrypt.org/getting-started/).
```apache
<IfModule mod_ssl.c>
<VirtualHost *:443>
...
SSLCertificateFile /etc/letsencrypt/live/hello.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/hello.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
</IfModule>
```
The configuration above match the settings for TLS generated by Let's Encrypt for Apache2.

View File

@ -1,25 +0,0 @@
# Vapor Cloud
The best way to deploy your Vapor application is [Vapor Cloud](https://vapor.cloud).
![Vapor Cloud Website](https://user-images.githubusercontent.com/1342803/31300830-fa6497b6-aac3-11e7-8852-33b30aa13186.png)
## Quick Start
If you already have the [Vapor Toolbox](../getting-started/toolbox.md) installed, then you can deploy your
Vapor app to the cloud with just one command.
```sh
vapor cloud deploy
```
![Vapor Toolbox Deploy](https://user-images.githubusercontent.com/1342803/31300850-14d7d8b0-aac4-11e7-8e42-84d406ffb76b.png)
!!! note:
Run the deploy command inside the root directory of your Vapor project (the one with the `Package.swift` file).
## Step-by-step Guide
Visit the [step-by-step guide](https://docs.vapor.cloud/quick-start/) on Vapor Cloud's docs for detailed
instructions on how to deploy your app to Vapor Cloud!

View File

@ -1,148 +0,0 @@
# Deploying with Nginx
Nginx is an extremely fast, battle tested, and easy-to-configure HTTP server and proxy. While Vapor supports directly serving HTTP requests with or without TLS, proxying behind Nginx can provide increased performance, security, and ease-of-use.
!!! note
We recommend proxying Vapor HTTP servers behind Nginx.
## Overview
What does it mean to proxy an HTTP server? In short, a proxy acts as a middleman between the public internet and your HTTP server. Requests come to the proxy and then it sends them to Vapor.
An important feature of this middleman proxy is that it can alter or even redirect the requests. For instance, the proxy can require that the client use TLS (https), rate limit requests, or even serve public files without talking to your Vapor application.
![nginx-proxy](https://cloud.githubusercontent.com/assets/1342803/20184965/5d9d588a-a738-11e6-91fe-28c3a4f7e46b.png)
### More Detail
The default port for receiving HTTP requests is port `80` (and `443` for HTTPS). When you bind a Vapor server to port `80`, it will directly receive and respond to the HTTP requests that come to your server. When adding a proxy like Nginx, you bind Vapor to an internal port, like port `8080`.
!!! note
Ports greater than 1024 do not require `sudo` to bind.
When Vapor is bound to a port besides `80` or `443`, it will not be accessible to the outside internet. You then bind Nginx to port `80` and configure it to route requests to your Vapor server bound at port `8080` (or whichever port you've chosen).
And that's it. If Nginx is properly configured, you will see your Vapor app responding to requests on port `80`. Nginx proxies the requests and responses invisibly.
## Install Nginx
The first step is installing Nginx. One of the great parts of Nginx is the tremendous amount of community resources and documentation surrounding it. Because of this, we will not go into great detail here about installing Nginx as there is almost definitely a tutorial for your specific platform, OS, and provider.
Tutorials:
- [How To Install Nginx on Ubuntu 14.04 LTS](https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-14-04-lts)
- [How To Install Nginx on Ubuntu 16.04](https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-16-04)
- [How to Deploy Nginx on Heroku](https://blog.codeship.com/how-to-deploy-nginx-on-heroku/)
- [How To Run Nginx in a Docker Container on Ubuntu 14.04](https://www.digitalocean.com/community/tutorials/how-to-run-nginx-in-a-docker-container-on-ubuntu-14-04)
### APT
Nginx can be installed through APT.
```sh
sudo apt-get update
sudo apt-get install nginx
```
Check whether Nginx was installed correctly by visiting your server's IP address in a browser
```sh
http://server_domain_name_or_IP
```
### Service
The service can be started or stopped.
```sh
sudo service nginx stop
sudo service nginx start
sudo service nginx restart
```
## Booting Vapor
Nginx can be started an stopped with the `sudo service nginx ...` commands. You will need something similar to start and stop your Vapor server.
There are many ways to do this, and they depend on which platform you are deploying to. Check out the [Supervisor](supervisor.md) instructions to add commands for starting and stopping your Vapor app.
## Configure Proxy
The configuration files for enabled sites can be found in `/etc/nginx/sites-enabled/`.
Create a new file or copy the example template from `/etc/nginx/sites-available/` to get started.
Here is an example configuration file for a Vapor project called `Hello` in the home directory.
```sh
server {
server_name hello.com;
listen 80;
root /home/vapor/Hello/Public/;
location @proxy {
proxy_pass http://127.0.0.1:8080;
proxy_pass_header Server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_header Server;
proxy_connect_timeout 3s;
proxy_read_timeout 10s;
}
}
```
This configuration file assumes the `Hello` project binds to port `8080` when started in production mode.
### Serving Files
Nginx can also serve public files without asking your Vapor app. This can improve performance by freeing up the Vapor process for other tasks under heavy load.
```sh
server {
...
# Serve all public/static files via nginx and then fallback to Vapor for the rest
try_files $uri @proxy;
location @proxy {
...
}
}
```
### TLS
Adding TLS is relatively straightforward as long as the certificates have been properly generated. To generate TLS certificates for free, check out [Let's Encrypt](https://letsencrypt.org/getting-started/).
```sh
server {
...
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/hello.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/hello.com/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security max-age=15768000;
...
location @proxy {
...
}
}
```
The configuration above are the relatively strict settings for TLS with Nginx. Some of the settings here are not required, but enhance security.

View File

@ -1,59 +0,0 @@
# Supervisor
[Supervisor](http://supervisord.org) is a process control system that makes it easy to start, stop, and restart your Vapor app.
## Install
```sh
sudo apt-get update
sudo apt-get install supervisor
```
## Configure
Each Vapor app on your server should have its own configuration file. For an example `Hello` project, the configuration file would be located at `/etc/supervisor/conf.d/hello.conf`
```sh
[program:hello]
command=/home/vapor/hello/.build/release/Run serve --env=production
directory=/home/vapor/hello/
user=www-data
stdout_logfile=/var/log/supervisor/%(program_name)-stdout.log
stderr_logfile=/var/log/supervisor/%(program_name)-stderr.log
```
As specified in our configuration file the `Hello` project is located in the home folder for the user `vapor`. Make sure `directory` points to the root directory of your project where the `Config/` folder is.
The `--env=production` flag will disable verbose logging and prioritize the `Config/production` sub folder of your configuration files.
### Environment
You can export variables to your Vapor app with supervisor.
```sh
environment=PORT=8123
```
Exported variables can be used in Vapor's configuration files with the `$` prefix.
`Config/production/servers.json `
```json
{
"port": "$PORT"
}
```
The above config file will start a server named `my-server` on the port number exported by supervisor. This is a great way to control how Vapor starts from the supervisor config scripts. Feel free to name the server whatever you like.
## Start
You can now load and start your app.
```sh
supervisorctl reread
supervisorctl add hello
supervisorctl start hello
```
!!! note
The `add` command may have already started your app.

View File

@ -1,241 +0,0 @@
# Database
A Fluent database is responsible for managing connections to your underlying data store and sending queries to the implementation-specific driver you have chosen.
## Drivers
By default, Fluent includes in-memory and SQLite drivers. There are several drivers available to add to your Vapor application.
### Available
| Type | Key | Package | Class | Official |
|------------|------------|------------------------------------------------------------------------------|-------------------------|----------|
| Memory | memory | [Fluent Provider](../fluent/package.md) | Fluent.MemoryDriver | Yes |
| SQlite | sqlite | [Fluent Provider](../fluent/package.md) | Fluent.SQLiteDriver | Yes |
| MySQL | mysql | [MySQLProvider](../mysql/package.md) | MySQLDriver.Driver | Yes |
| PostgreSQL | postgresql | [PostgreSQLProvider](https://github.com/vapor-community/postgresql-provider) | PostgreSQLDriver.Driver | No |
| MongoDB | N/A | [MongoProvider](https://github.com/vapor-community/mongo-provider) | N/A | No |
Click on the provider package for more information about how to use it.
You can search for a list of available [Vapor database providers](https://github.com/search?utf8=✓&q=topic%3Avapor-provider+topic%3Adatabase&type=Repositories) on GitHub.
## Droplet
You can access the database from the Droplet.
```swift
drop.database // Database?
```
## Preparations
Most databases, like SQL databases, require the schema for a model to be created before it is stored.
Adding a preparation to your model will allow you to prepare the database while your app boots.
```swift
extension User: Preparation {
/// Prepares a table/collection in the database
/// for storing Users
static func prepare(_ database: Database) throws {
try database.create(self) { builder in
builder.id()
builder.string("name")
builder.int("age")
}
}
/// Undoes what was done in `prepare`
static func revert(_ database: Database) throws {
try database.delete(self)
}
}
```
The above prepare statement results in SQL similar to the following:
```sql
CREATE TABLE `users` (`id` INTEGER PRIMARY KEY NOT NULL, `name` TEXT NOT NULL, `age` INTEGER NOT NULL)
```
Once you have created you preparation, add it to the Config's prepratations array.
```swift
config.preparations.append(User.self)
```
### Create
The following methods are available on while creating and modifing the database.
| Method | Type |
|-----------|--------------------|
| id | Primary Identifier |
| foreignId | Foreign Identifier |
| int | Integer |
| string | String |
| double | Double |
| bool | Boolean |
| bytes | Data |
| date | Date + Time |
You can use any of these methods on a builder inside `.create()`.
```swift
try database.create(self) { builder in
builder.double("latitude")
builder.double("longitude")
}
```
### Foreign Keys
Foreign keys are automatically added with `.foreignId()`. To add a foreign key manually, use the
`.foreignKey` method.
```swift
try database.create(self) { builder in
builder.foreignKey("user_id", references: "id", on: User.self)
}
```
To disable automatic foreign keys, set `autoForeignKeys` to false in the `Config/fluent.json` file.
```json
{
"autoForeignKeys": false
}
```
### Modifier
Existing schema can be modified using the `.modify()` property. All of the methods from `.create()`
are available here as well.
```swift
try database.modify(self) { builder in
builder.string("name")
builder.delete("age")
}
```
### Migrations
Other times, you may want to make some modifications to your data set while migrating to a new version or
just performing general cleanup.
```swift
struct DeleteOldEntries: Preparation {
static func prepare(_ database: Database) throws {
try Log.makeQuery().filter(...).delete()
}
...
}
```
### Run
Your preparations will run every time you run your application. You can run your preparations without booting
your server by calling:
```sh
vapor run prepare
```
### Revert
Use the revert method to undo any work you did in the prepare method.
```swift
extension User: Preparation {
...
static func revert(_ database: Database) throws {
try database.delete(self)
}
}
```
You can run the reversions by calling:
```
vapor run prepare --revert
```
This will revert the latest batch of preparations. To revert the entire database, run the following:
```
vapor run prepare --revert --all
```
## Log
Logging queries is a great way to find optimizations for your application and track down bugs.
The easiest way to log queries is to enable logging in your `fluent.json` file.
`Config/fluent.json`
```json
{
...,
"log": true,
...
}
```
This will emit info-level logs for all database queries.
### Custom
You can also hook into the database's query logging callback to execute custom logic.
```swift
drop.database?.log = { query in
print(query)
}
```
You can assign a closure to the `log` property on the database. Any time a query is run, the closure
will be called with a `QueryLog` object containing a string describing the statement and the time it ran.
## Transactions
Transactions allow you to group multiple queries into one single unit of work. If any one of the
queries experiences a problem, the entire transaction will be rolled back.
```swift
drop.database?.transaction { conn in
try user.pets.makeQuery(conn).delete()
try user.makeQuery(conn).delete()
}
```
Drivers that do not support transactions will throw an error if this method is called.
You can use the `.makeQuery(_: Executor)` method to create queries that will run on the
connection supplied to the closure.
!!! warning
You must use the connection supplied to the closure for queries you want to include
in the transaction.
## Indexes
An index is a copy of selected columns of data from a table that can be searched very efficiently.
You can add them to your database by calling `.index()`
```swift
try database.index("name", for: User.self)
```
You can delete them by calling `.deleteIndex()`
```swift
try database.deleteIndex("name", for: User.self)
```

View File

@ -1,167 +0,0 @@
# Getting Started with Fluent
Fluent provides an easy, simple, and safe API for working with your persisted data. Each database table/collection is represented by a `Model` that can be used to interact with the data. Fluent supports common operations like creating, reading, updating, and deleting models. It also supports more advanced operations like joining, relating, and soft deleting.
!!! note
Don't forget to add `import FluentProvider` (or your other database provider) to the top of your Swift files.
Fluent ships with SQLite by default. You can use SQLite to quickly scaffold your application with the in-memory database it provides. This is enabled by default in Vapor's default template. To learn more about configuring your database, check out the available [drivers](#drivers).
## Creating a Model
Models are the Swift representations of the data in your database. As such, they are central to most of Fluent's APIs.
Let's take a look at what a simple model looks like.
```swift
final class Pet: Model {
var name: String
var age: Int
let storage = Storage()
init(row: Row) throws {
name = try row.get("name")
age = try row.get("age")
}
init(name: String, age: Int) {
self.name = name
self.age = age
}
func makeRow() throws -> Row {
var row = Row()
try row.set("name", name)
try row.set("age", age)
return row
}
}
```
Here we are creating a simple class `Pet` with a name and an age. We will add a simple init method for creating new pets.
### Storage
The `storage` property is there to allow Fluent to store extra information on your model--things like the model's database id.
### Row
The `Row` struct represents a database row. Your models should be able to parse from and serialize to database rows.
#### Parse
Here's the code for parsing the Pet from the database.
```swift
final class Pet: Model {
...
init(row: Row) throws {
name = try row.get("name")
age = try row.get("age")
}
}
```
#### Serialize
Here's the code for serializing the Pet to the database.
```swift
final class Pet: Model {
...
func makeRow() throws -> Row {
var row = Row()
try row.set("name", name)
try row.set("age", age)
return row
}
}
```
## Preparing the Database
In order to use your model, you may need to prepare your database with an appropriate schema.
### Preparation
You can do this by conforming your model to `Preparation`.
```swift
extension Pet: Preparation {
static func prepare(_ database: Database) throws {
try database.create(self) { pets in
pets.id()
pets.string("name")
pets.int("age")
}
}
static func revert(_ database: Database) throws {
try database.delete(self)
}
}
```
Here we are creating a simple table that will look like this:
| id | name | age |
|--------------------------|--------|-----|
| &lt;database id type&gt; | string | int |
### Add to Droplet
Now you can add your model to the config's preparations so the database is prepared when your application boots.
```swift
import Vapor
import FluentProvider
let config = try Config()
config.preparations.append(Pet.self)
let drop = try Droplet(config)
...
```
## Using Models
Now that we have created our model and prepared the database, we can use it to save and fetch data from the database.
### Save
To save a model, call `.save()`. A new identifier for the model will automatically be created.
```swift
let dog = Pet(name: "Spud", age: 5)
try dog.save()
print(dog.id) // the newly saved pet's id
```
### Find
You can fetch a model from the database using it's ID.
```swift
guard let dog = try Pet.find(42) else {
throw Abort.notFound
}
print(dog.name) // the name of the dog with id 42
```
### Filter
You can also search for models using filters.
```swift
let dogs = try Pet.makeQuery().filter("age", .greaterThan, 2).all()
print(dogs) // all dogs older than 2
```
## Drivers
Check out the [database](database.md) section for more information about different database drivers you can use with Fluent.

View File

@ -1,365 +0,0 @@
# Model
Models are the Swift representations of the data in your database. As such, they are central to most of Fluent's APIs.
This guide is an overview of the protocol requirements and methods associated with models.
!!! seealso
Check out the [getting started](getting-started.md) guide for an introductory overview on using models.
## CRUD
Models have several basic methods for creating, reading, updating, and deleting.
### Save
Persists the entity into the data store and sets the `id` property.
```swift
let pet = Pet(name: "Spud", age: 2)
try pet.save()
```
### Find
Finds the model with the supplied identifier or returns `nil`.
```swift
guard let pet = try Pets.find(42) else {
throw Abort.notFound
}
print(pet.name)
```
### Delete
Deletes the entity from the data store if the entity has previously been fetched or saved.
```swift
try pet.delete()
```
### All
Returns all entities for this model.
```swift
for pet in try Pets.all() {
print(pet.name)
}
```
### Count
Returns a count of all entities for this model.
```swift
let count = try Pets.count()
```
### Chunk
Returns chunked arrays of a supplied size for all of the entities for this model.
This is a great way to parse through all models of a large data set.
```swift
try Pets.chunk(20) { pets in
//
}
```
### Query
Creates a `Query` instance for this `Model`.
```swift
let query = try Pet.makeQuery()
```
To learn more about crafting complex queries, see the [query](query.md) section.
## Timestamps
To add timestamps to your model, simply conform it to `Timestampable`.
```swift
extension User: Timestampable { }
```
You can access the updated at and created at times on any model instance.
```swift
user.updatedAt // Date?
user.createdAt // Date?
```
When filtering or sorting on the timestamp data, you can use the timestamp keys from the class.
```swift
let newUsers = try User
.makeQuery()
.filter(User.createdAtKey, .greaterThan, ...)
.all()
```
You can also override the timestamp keys if you have custom needs.
```swift
extension User: Timestampable {
static var updatedAtKey: String { return "custom_updated_at" }
static var createdAtKey: String { return "custom_created_at" }
}
```
### Migration
`Timestampable` models will automatically have created at and updated at keys added during
[database create](database.md#Create) calls.
Should you need to manually add `Timestampable` to an existing model, you can use the `date()` method
in a [migration](database.md#Migrations).
```swift
database.modify(User.self) { builder in
builder.date(User.createdAtKey)
builder.date(User.updatedAtKey)
}
```
## Soft Delete
Soft delete is a way of "deleting" a model from all fetch and update queries to Fluent but not actually deleting the model from the database. Soft deleted models can also be restored.
To make your model soft deletable, simply conform it to `SoftDeletable`.
```swift
extension User: SoftDeletable { }
```
Once your model is soft deletable, all calls to `delete()` will set the deleted at flag instead of actually
deleting the model.
To restore a model, call `.restore()`. To actually delete a model from the database, call `.forceDelete()`.
You can also override the soft delete key if you have custom needs.
```swift
extension User: SoftDeletable {
static var deletedAtKey: String { return "custom_deleted_at" }
}
```
### Including Deleted
When a model is soft deleted, it will not be affected by any queries made with the Fluent query builder.
To include soft deleted models, for instance if you want to restore them, use the `.withSoftDeleted()` method
on the query builder.
```swift
let allUsers = try User.makeQuery().withSoftDeleted().all()
```
### Lifecycle
You can hook into the soft delete events of a model.
```swift
extension User: SoftDeletable {
func willSoftDelete() throws { ... }
func didSoftDelete() { ... }
func willForceDelete() throws { ... }
func didForceDelete() { ... }
func willRestore() throws { ... }
func didRestore() { ... }
}
```
!!! note:
Throwing during a `will` hook will prevent the action from happening.
### Migration
`SoftDeletable` models will automatically have a deleted at key added during
[database create](database.md#create) calls.
Should you need to manually add `SoftDeletable` to an existing model, you can use the `date()` method
in a [migration](database.md#migrations).
```swift
database.modify(User.self) { builder in
builder.date(User.deletedAtKey, optional: true)
}
```
## Convenience
### Assert Exists
The identifier property of a model is optional since models may not have been saved yet.
You can get the identifier or throw an error if the model has not been saved yet by calling `assertExists()`.
```swift
let id = try pet.assertExists()
print(id) // not optional
```
## Life Cycle
The following life-cycle methods can be implemented on your model to hook into internal operations.
```swift
/// Called before the entity will be created.
/// Throwing will cancel the creation.
func willCreate() throws
/// Called after the entity has been created.
func didCreate()
/// Called before the entity will be updated.
/// Throwing will cancel the update.
func willUpdate() throws
/// Called after the entity has been updated.
func didUpdate()
/// Called before the entity will be deleted.
/// Throwing will cancel the deletion.
func willDelete() throws
/// Called after the entity has been deleted.
func didDelete()
```
!!! note
Throwing in a `willFoo()` method will cancel the operation.
Here's an example of implementing the `didDelete` method.
```swift
final class Pet: Model {
...
func didDelete() {
print("Deleted \(name)")
}
}
```
## Entity
Entity is the base Fluent protocol that Model conforms to. It is responsible for providing all information the
database or query may need when saving, fetching, or deleting your models.
### Name
The singular relational name of this model. Also used for internal storage. Example: Pet = "pet".
This value should usually not be overriden.
```swift
final class Pet: Model {
static let name = "pet"
}
```
### Entity
The plural relational name of this model. Used as the collection or table name.
Example: Pet = "pets".
This value should be overriden if the table name for your model is non-standard.
```swift
final class Pet: Model {
static let entity = "pets"
}
```
### ID Type
The type of identifier used for both the local and foreign id keys.
Example: uuid, integer, etc.
This value should be overriden if a particular model in your database uses a different ID type.
```swift
final class Pet: Model {
static let idType: IdentifierType = .uuid
}
```
This can also be overridden at the database level using config.
`Config/fluent.json`
```json
{
"idType": "uuid"
}
```
Or programatically.
```swift
drop.database?.idType = .uuid
```
### Key Naming Convention
The naming convetion to use for foreign id keys, table names, etc.
Example: snake_case vs. camelCase.
This value should be overridden if a particular model in your database uses a different key naming convention.
```swift
final class Pet: Model {
static let keyNamingConvention = .snake_case
}
```
This can also be overridden at the database level using config.
`Config/fluent.json`
```json
{
"keyNamingConvention": "snake_case"
}
```
Or programatically.
```swift
drop.database?.keyNamingConvention = .snake_case
```
### ID Key
The name of the column that corresponds to this entity's identifying key.
The default is 'database.driver.idKey', and then "id"
```swift
final class Pet: Model {
static let idKey = "id"
}
```
### Foreign ID Key
The name of the column that points to this entity's id when referenced from other tables or collections.
Example: "foo_id".
```swift
final class Pet: Model {
static let foreignIdKey = "pet_id"
}
```

View File

@ -1,50 +0,0 @@
# Using Fluent
This section outlines how to import the Fluent package both with or without a Vapor project.
## With Vapor
Fluent comes included with most Vapor templates. However, if you have created a project from scratch you will need to add the provider to your `Package.swift` file.
```swift
import PackageDescription
let package = Package(
name: "Project",
dependencies: [
.Package(url: "https://github.com/vapor/vapor.git", majorVersion: 2),
.Package(url: "https://github.com/vapor/fluent-provider.git", majorVersion: 1)
],
exclude: [ ... ]
)
```
The Fluent provider package adds Fluent to your project and adds some additional, Vapor-specific conveniences like HTTP conformances.
Using `import FluentProvider` will import both Fluent and Fluent's Vapor-specific APIs.
## Without Vapor
Fluent is a powerful, pure-Swift ORM that can be used with any Server-Side Swift framework. To include it in your package, add it to your `Package.swift` file.
```swift
import PackageDescription
let package = Package(
name: "Project",
dependencies: [
...
.Package(url: "https://github.com/vapor/fluent.git", majorVersion: 2)
],
exclude: [ ... ]
)
```
Use `import Fluent` to access Fluent's APIs.
!!! warning
`Model` is a Vapor + Fluent type, use `Entity` instead.
## Drivers
Fluent drivers allow Fluent models and queries to communicate with various database technologies like MySQL or Mongo. For a full list of drivers, check out the [`fluent-driver`](https://github.com/search?utf8=✓&q=topic%3Afluent-driver&type=Repositories) tag on GitHub.

View File

@ -1,273 +0,0 @@
# Query
Fluent's query builder provides a simple interface for creating complex database queries. The `Query` class itself (raw queries excluded) is the sole method by which Fluent communicates with your database.
## Make
You can create a new query builder from any model class.
```swift
let query = try Post.makeQuery()
```
You can also create queries from an instance. This is especially useful if you need to use a special
database connection (like for [transactions](database.md#Transactions)) to save or update a model.
```swift
guard let post = try Post.find(42) else { ... }
post.content = "Updated"
let query = try post.makeQuery(conn).save()
```
## Fetch
You have multiple options for fetching the results of a query.
### All
The simplest option, `.all()` returns all rows relevant to the query.
```
let users = try User.makeQuery().filter(...).all()
```
### First
You can take only the first row as well with `.first()`.
```
let user = try User.makeQuery().filter(...).first()
```
Fluent will automatically limit the results to `1` to increase
the performance of the query.
### Chunk
If you want to fetch a large amount of models from the database, using `.chunk()` can help reduce the
amount of memory required for the query by fetching chunks of data at a time.
```
User.makeQuery().filter(...).chunk(32) { users in
print(users)
}
```
## Filter
Filters allow you to choose exactly what subset of data you want to modify or fetch. There are three different
types of filters.
### Compare
Compare filters perform a comparison between a field on your model in the database and a supplied value.
```swift
try query.filter("age", .greaterThanOrEquals, 21)
```
You can also use operators.
```swift
try query.filter("age" >= 21)
```
| Case | Operator | Type |
|----------------------|----------|------------------------|
| .equals | == | Equals |
| .greaterThan | > | Greater Than |
| .lessThan | < | Less Than |
| .greaterThanOrEquals | >= | Greater Than Or Equals |
| .lessThanOrEquals | <= | Less Than Or Equals |
| .notEquals | != | Not Equals |
| .hasSuffix | | Has Suffix |
| .hasPrefix | | Has Prefix |
| .contains | | Contains |
| .custom(String) | | Custom |
!!! tip
You can omit the comparison type for `.equals`, e.g., `query.filter("age", 23)`
### Subset
You can also filter by fields being in a set of data.
```swift
try query.filter("favoriteColor", in: ["pink", "blue"])
```
Or the opposite.
```swift
try query.filter("favoriteColor", notIn: ["brown", "black"])
```
### Group
By default, all query filters are joined by AND logic. You can create groups of filters within
your query that are joined with AND or OR logic.
```swift
try query.or { orGroup in
try orGroup.filter("age", .greaterThan, 75)
try orGroup.filter("age", .lessThan, 18)
}
```
This will result in SQL similar to the following:
```sql
SELECT * FROM `users` WHERE (`age` > 75 OR `age` < 18);
```
`.and()` is also available in case you need to switch back to joining filters with AND nested inside of an OR.
#### Complex Example
```swift
let users = try User
.makeQuery()
.filter("planetOfOrigin", .greaterThan, "Earth")
.or { orGroup in
orGroup.and { andGroup in
andGroup.filter("name", "Rick")
andGroup.filter("favoriteFood", "Beer")
}
orGroup.and { andGroup in
andGroup.filter("name", "Morty")
andGroup.filter("favoriteFood", "Eyeholes")
}
}
.all()
```
This will result in SQL similar to the following:
```sql
SELECT * FROM `users`
WHERE `planetOfOrigin` = 'Earth' AND (
(`name` = 'Rick' AND `favoriteFood` = 'Beer')
OR (`name` = 'Morty' AND `favoriteFood` = 'Eyeholes')
)
```
!!! note
Keep in mind that the AND/OR logic for a group applies only to the filters added _within_ the group. All filters outside of a filter group will be joined by AND.
### Raw
Raw filters can be used to filter by values that should not be parameterized.
```swift
try query.filter(raw: "date >= CURRENT_TIMESTAMP")
```
### NodeConvertible Conformance
Filters can be converted to and from Node objects, which allows filters to be specified via JSON and other NodeRepresentable formats. This makes it very easy if you want to allow a consumer API to filter your entities.
Example:
```json
{
"entity":"MyApp.User",
"method":{
"type":"group",
"relation":"and",
"filters":[
{
"entity":"MyApp.User",
"method":{
"type":"compare",
"comparison":"greaterThanOrEquals",
"field":"age",
"value":18
}
},
{
"entity":"MyApp.User",
"method":{
"type":"compare",
"comparison":"equals",
"field":"gender",
"value":"male"
}
}
]
}
}
```
!!! note
You must include the module name in the entity field. "MyModule.MyEntity"
## Distinct
To select only distinct models from the database, add `.distinct()` to your query.
```swift
try query.distinct()
```
## Limit / Offset
To limit or offset your query, use the `.limit()` method.
```swift
try query.limit(20, offset: 5)
```
## Sort
To sort the results of your query, use the `.sort()` method.
```swift
try query.sort("age", .descending)
```
You can sort on multiple columns at once by chaining your `.sort()` calls.
```swift
try query.sort("age", .descending).sort("shoe_size")
```
## Join
You can join two model tables together, which is useful if you want to filter one model by a property of another.
For example, let's say you have a table of Employees which belong to Departments. You want to know which
Departments contain Employees who have completed ten years of service.
First you use the `.join()` method on a Department query to join it with the Employee table. Next you chain a
`.filter()` on to the query. Bear in mind you need to explicitly pass the 'joined' model to the filter, otherwise
Fluent will try to filter on the 'base' model.
```swift
let departments = try Department.makeQuery()
.join(Employee.self)
.filter(Employee.self, "years_of_service" >= 10)
```
Fluent will work out the relationship fields for you, but you can also specify them yourself with the `baseKey`
and `joinedKey` method parameters, where `baseKey` is the identifier field on the 'base' model (the Department)
and `joinedKey` is the foreign key field on the 'joined' model (the Employee) which relates back to the 'base' model.
!!! tip
Fluent supports both inner and outer joins; use the invocation `.join(kind: .outer, MyModel.self)`
## Raw
Should you need to perform a query that the query builder does not support, you can use the raw query.
```swift
try drop.database?.raw("SELECT @@version")
```
You can also use the database of a given model.
```swift
User.database?.raw("SELECT * FROM `users`")
```
Besides providing a more expressive interface for querying your database, the query builder also takes measures to increase security by automatically sanitizing input. Because of this, try to use the query class wherever you can over performing raw queries.

View File

@ -1,245 +0,0 @@
# Relations
Fluent relations allow you to relate your models in three different ways:
| Type | Relations |
|--------------|-------------------|
| One to One | Parent / Child |
| One to Many | Parent / Children |
| Many to Many | Siblings |
## One to Many
We'll start with one-to-many since it's the easiest type of relation to understand.
Take the following database schema:
`users`
| id | name |
|-----------------|--------|
| &lt;id type&gt; | string |
`pets`
| id | name | user_id |
|-----------------|--------|-----------------|
| &lt;id type&gt; | string | &lt;id type&gt; |
!!! seealso
Visit the [database preparations](database.md#preparations) guide for more information
on how to create schema.
Here each pet has exactly one owner (a user) and each owner can have multiple pets.
This is a one-to-many relationship. One owner has many pets.
!!! tip
Use the `builder.foreignId()` to create foreign ids like `user_id`. This will automatically
create foreign key constraints and follow pre-set key naming conventions.
### Children
To access the user's pets, we will use the `Children` relation.
```swift
extension User {
var pets: Children<User, Pet> {
return children()
}
}
```
Imagine the children relation as `Children<Parent, Child>` or `Children<From, To>`.
Here we are relating _from_ the user type _to_ the pet type.
We can now use this relation to get all of the user's pets.
```swift
let pets = try user.pets.all() // [Pet]
```
This will create SQL similar to:
```sql
SELECT * FROM `pets` WHERE `user_id` = '...';
```
Relations work similarly to [queries](query.md).
```swift
let pet = try user.pets.filter("name", "Spud").first()
```
### Parent
To access a pet's owner from the pet, we will use the `Parent` relation.
```swift
extension Pet {
let userId: Identifier
...
var owner: Parent<Pet, User> {
return parent(id: userId)
}
}
```
Imagine the parent relation as `Parent<Child, Parent>` or `Parent<From, To>`.
Here we are relating _from_ the pet type _to_ the parent type.
!!! note
Notice the `Parent` relation requires an identifier to be passed in.
Make sure to load this identifier in your model's `init(row:)` method.
We can now use this relation to get the pet's owner.
```swift
let owner = try pet.owner.get() // User?
```
#### Migration
Adding a parent identifier to the child table can be done using the `.parent()` method on
the schema builder.
```swift
try database.create(Pet.self) { builder in
...
builder.parent(User.self)
}
```
## One to One
One-to-one relations work exactly the same as one-to-many relations. You can use the
code from the previous example and simply call `.first()` and all calls from the parent type.
However, you can add a convenience for doing this. Let's assume we wanted to change the previous
example from one-to-many to one-to-one.
```swift
extension User {
func pet() throws -> Pet? {
return try children().first()
}
}
```
## Many to Many
Many to many relations require a table in between to store which model is related to which.
This table is called a pivot table.
You can use any entity you want as a pivot, but Fluent provides a default one called `Pivot`.
Take the following schema.
`pets`
| id | name |
|-----------------|--------|
| &lt;id type&gt; | string |
`pet_toy`
| id | pet_id | toy_id |
|-----------------|-----------------|-----------------|
| &lt;id type&gt; | &lt;id type&gt; | &lt;id type&gt; |
`toys`
| id | name |
|-----------------|--------|
| &lt;id type&gt; | string |
Here each pet can own many toys and each toy can belong to many pets. This is a many-to-many relationship.
### Siblings
To represent this many-to-many relationship, we will use the `Siblings` relation.
```swift
extension Pet {
var toys: Siblings<Pet, Toy, Pivot<Pet, Toy>> {
return siblings()
}
}
```
Imagine the siblings relations as `Siblings<From, To, Through>`.
Here we are relating _from_ the pet type _to_ the toy type _through_ the pet/toy pivot.
!!! note
The generic syntax might look a little intimidating at first, but it allows for a very powerful API.
With this relation added on pets, we can fetch a pet's toys.
```swift
let toys = pet.toys.all() // [Toy]
```
The siblings relation works similarly to [queries](query.md) and parent/children relations.
#### Migration
If you are using a `Pivot` type, you can simply add it to your Droplet's preparation array.
```swift
drop.preparations.append(Pivot<Pet, Toy>.self)
```
If you are using a `Pivot` for your "through" model, it will also have methods for adding and removing models from the relation.
#### Add
To add a new model to the relation, use the `.add()` method.
```swift
try pet.toys.add(toy)
```
!!! note
The newly created pivot will be returned.
#### Remove
To remove a model from being related, use the `.remove()` method.
```swift
try pet.toys.remove(toy)
```
#### Is Attached
To check if a model is related, use the `.isAttached()` method.
```swift
if try pet.toys.isAttached(to: toy) {
// it is attached
}
```
#### Custom Through
You can use any entity type as the "through" entity in your siblings relation.
```swift
extension User {
var posts: Siblings<User, Post, Comment> {
return siblings()
}
}
```
In the above example we are pivoting on the comments entity to retreive all posts the user
has commented on.
As long as the "through" entity has a `user_id` and `post_id`, the siblings relation will work.
!!! note
If the `Comment `entity does not conform to `PivotProtocol`, the
`add`, `remove`, and `isAttached` methods will not be available.

View File

@ -1,169 +0,0 @@
# Hello, World
This section assumes you have installed Swift 3.1 and the Vapor Toolbox and have verified they are working.
!!! tip
Note: If you don't want to use the Toolbox, follow the [manual guide](manual.md).
## New Project
Let's start by creating a new project called "Hello, World".
```sh
vapor new Hello --template=api
```
Vapor's folder structure will probably look familiar to you if you have worked with other web frameworks.
```
Hello
├── Config
│   ├── app.json
│   ├── crypto.json
│   ├── droplet.json
│   ├── fluent.json
│   └── server.json
├── Package.pins
├── Package.swift
├── Public
├── README.md
├── Sources
│   ├── App
│   │   ├── Config+Setup.swift
│   │   ├── Controllers
│   │   │   └── PostController.swift
│   │   ├── Droplet+Setup.swift
│   │   ├── Models
│   │   │   └── Post.swift
│   │   └── Routes
│   │   └── Routes.swift
│   └── Run
│   └── main.swift
├── Tests
│   ├── AppTests
│   │   ├── PostControllerTests.swift
│   │   ├── RouteTests.swift
│   │   └── Utilities.swift
│   └── LinuxMain.swift
├── circle.yml
└── license
```
For our Hello, World project, we will be focusing on the `Routes.swift` file.
```
Hello
└── Sources
└── App
└── Routes.swift
```
!!! tip
The `vapor new` command creates a new project with examples and comments about how to use the framework. You can delete these if you want.
## Code
### Droplet
Look for the following line in the `Routes.swift` file.
```swift
func setupRoutes() throws
```
This method is where all the routes for our application will be added.
### Routing
In the scope of the `setupRoutes` method, look for the following statement.
```swift
get("plaintext") { req in
return "Hello, world!"
}
```
This creates a new route that will match all `GET` requests to `/plaintext`.
All route closures are passed an instance of [Request](../http/request.md) that contains information such as the URI requested and data sent.
This route simply returns a string, but anything that is [ResponseRepresentable](../http/response-representable.md) can be returned. Learn more in the [Routing](../routing/overview.md) section of the guide.
!!! tip
Xcode autocomplete may add extraneous type information to your closure's input arguments. This can be deleted to keep the code clean. If you'd like to keep the type information add `import HTTP` to the top of the file.
## Compile & Run
### Building
A big part of what makes Vapor so great is Swift's state of the art compiler. Let's fire it up. Make sure you are in the root directory of the project and run the following command.
```swift
vapor build
```
!!! note
`vapor build` runs `swift build` in the background.
The Swift Package Manager will first start by downloading the appropriate dependencies from git. It will then compile and link these dependencies together.
When the process has completed, you will see `Building Project [Done]`
!!! tip
If you see a message like `unable to execute command: Killed`, you need to increase your swap space. This can happen if you are running on a machine with limited memory.
#### Release
Building your application in release mode takes longer, but increases performance.
```sh
vapor build --release
```
### Serving
Boot up the server by running the following command.
```sh
vapor run serve
```
You should see a message `Server starting...`.
You can now visit `localhost:8080/plaintext` in your browser or run
```sh
curl localhost:8080/plaintext
```
!!! note
Certain port numbers require super user access to bind. Simply run `sudo vapor run` to allow access. If you decide to run on a port besides `80`, make sure to direct your browser accordingly.
### Hello, World
You should see the following output in your browser window.
```
Hello, world!
```
!!! success
Like Vapor so far? Click the button below and star the repo to help spread the word!
<iframe src="https://ghbtns.com/github-btn.html?user=vapor&repo=vapor&type=star&count=true&size=large" frameborder="0" scrolling="0" width="160px" height="30px"></iframe>
#### Production
Serving your application in the production environment increases its security and performance.
```sh
vapor run serve --env=production
```
Some debug messages will be silenced while in the production environment, so make sure to check your logs for errors.
!!! warning
If you compiled your application with `--release`, make sure to add that flag to the `vapor run` command as well. e.g., `vapor run serve --env=production --release`.
For more information on deploying your code, check out the [deploy section](../deploy/nginx.md).

View File

@ -1,73 +0,0 @@
# Install on macOS
To use Vapor on macOS, you just need to have Xcode 8 or later installed.
## Install Xcode
Install [Xcode 9](https://itunes.apple.com/us/app/xcode/id497799835?mt=12) from the Mac App Store.
[![Xcode 9](https://cloud.githubusercontent.com/assets/1342803/18537674/2ddd8e9c-7ad5-11e6-9bc2-7155d57d20ec.png)](https://itunes.apple.com/us/app/xcode/id497799835?mt=12)
(Xcode 8 is the minimum required to use Vapor 2.0 on macOS. If you have an Apple Developer membership you can download older versions of Xcode from Apple's developer [downloads page](https://developer.apple.com/download/more/))
### Open Xcode
After Xcode has been downloaded, you must open it to finish the installation. This may take a while.
## Verify Swift Installation
Double check the installation was successful by opening Terminal and running:
```sh
eval "$(curl -sL check.vapor.sh)"
```
## Install Vapor
Now that you have Swift 4 (or Swift 3.1 if you installed Xcode 8), let's install the Vapor toolbox.
The toolbox includes all of Vapor's dependencies as well as a handy CLI tool for creating new projects.
### Install Homebrew
If you don't already have Homebrew installed, install it! It's incredibly useful for installing software dependencies like OpenSSL, MySQL, Postgres, Redis, SQLite, and more.
```sh
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```
For more information on installing Homebrew, visit [brew.sh](https://brew.sh).
### Add Homebrew Tap
Vapor's Homebrew tap will give your Homebrew installation access to all of Vapor's macOS packages.
```sh
brew tap vapor/homebrew-tap
brew update
```
### Install
Now that you've added Vapor's tap, you can install Vapor's toolbox and dependencies.
```sh
brew install vapor
```
### Upgrade
If you've previously installed Vapor upgrades to Homebrew and Vapor may be required to work with the latest versions of macOS, Swift, or the instructions in this guide.
```sh
brew update
brew upgrade vapor
```
## Next
Learn more about the Vapor toolbox CLI in the [Toolbox section](toolbox.md) of the Getting Started section.
## Swift.org
Check out [Swift.org](https://swift.org)'s extensive guides if you need more detailed instructions for installing Swift.

View File

@ -1,68 +0,0 @@
# Install on Ubuntu
Installing Vapor on Ubuntu only takes a couple of minutes.
## Supported
Vapor supports the same versions of Ubuntu that Swift supports.
| Version | Codename |
|---------|--------------|
| 16.10 | Yakkety Yak |
| 16.04 | Xenial Xerus |
| 14.04 | Trusty Tahr |
## APT Repo
Add Vapor's APT repo to get access to all of Vapor's system packages.
### Quick Script
Easily add Vapor's APT repo with this handy script.
```sh
eval "$(curl -sL https://apt.vapor.sh)"
```
!!! note
This command requires `curl` which can be installed using `sudo apt-get install curl`
### Dockerfile
When configuring Ubuntu from a Dockerfile, adding the APT repo can be done via this command:
```sh
RUN /bin/bash -c "$(wget -qO- https://apt.vapor.sh)"
```
### Manual
Or add the repo manually.
```sh
wget -q https://repo.vapor.codes/apt/keyring.gpg -O- | sudo apt-key add -
echo "deb https://repo.vapor.codes/apt $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/vapor.list
sudo apt-get update
```
## Install Vapor
Now that you have added Vapor's APT repo, you can install the required dependencies.
```sh
sudo apt-get install swift vapor
```
## Verify Installation
Double check the installation was successful by running:
```sh
eval "$(curl -sL check.vapor.sh)"
```
## Next
Learn more about the Vapor toolbox CLI in the [Toolbox section](toolbox.md) of the Getting Started section.
## Swift.org
Check out [Swift.org](https://swift.org)'s guide to [using downloads](https://swift.org/download/#using-downloads) if you need more detailed instructions for installing Swift 3.1.

View File

@ -1,98 +0,0 @@
# Manual Quickstart
Learn how to create a Vapor project _without_ the Toolbox using just Swift 3.1 and the Swift Package Manager (SPM).
This document assumes that you have Swift 3.1 installed, if not please refer to [Swift.org](https://swift.org/getting-started/#installing-swift) before you can continue.
!!! tip
If you'd prefer to use the Toolbox, follow the toolbox guide [here](hello-world.md).
## Make new project using SwiftPM
Open your terminal
!!! note
For our example, we'll be using the Desktop folder.
```bash
cd ~/Desktop
mkdir Hello
cd Hello
swift package init --type executable
```
Your folder structure should look like this:
```
├── Package.swift
├── Sources
│   └── main.swift
└── Tests
```
### Edit `Package.swift`
Open your `Package.swift` file:
```bash
open Package.swift
```
And add Vapor as a dependency. Here's how your `Package.swift` should look like.
```swift
// swift-tools-version:3.1
import PackageDescription
let package = Package(
name: "Hello",
dependencies: [
.Package(url: "https://github.com/vapor/vapor.git", majorVersion: 2)
]
)
```
!!! warning
We try to keep this document up to date, however, you can view latest releases [here](https://github.com/vapor/vapor/releases).
### Edit `main.swift`
A simple hello world:
```swift
import Vapor
let drop = try Droplet()
drop.get("hello") { req in
return "Hello Vapor"
}
try drop.run()
```
### Compile & Run (Development)
The first `build` command can take a while to fetch dependencies.
```bash
swift build
.build/debug/Hello serve
```
!!! warning
If different, replace `Hello` above with the name of your executable (as defined in `Package.swift`).
### Production
Compiling in Swift's release mode and setting Vapor's environment to production will make your app more secure and performant.
```sh
swift build --configuration release
.build/release/Hello serve --env=production
```
### View
Go to your favorite browser and visit `http://localhost:8080/hello`

View File

@ -1,72 +0,0 @@
# Install Toolbox
Vapor's command line interface provides shortcuts and assistance for common tasks.
<img width="682" alt="Vapor Toolbox" src="https://cloud.githubusercontent.com/assets/1342803/23553208/26af9a0e-0020-11e7-8ed5-1ce09407ae8e.png">
!!! tip
If you do not want to use the Toolbox or templates, checkout the [Manual](manual.md) quickstart.
## Help
Help prints useful information about available commands and flags. You can also run the `--help` option on any Toolbox command.
```sh
vapor --help
```
### Application Commands
The `vapor run` command is a special toolbox command that forwards to your Vapor application.
Once you've built your application with `vapor build` you can use `vapor run serve` to boot your application, or `vapor run help` to view all available application-level commands. This includes custom commands you may have added to your application.
!!! warning
Using `vapor run --help` will provide information about the `run` command itself and will not forward to your Vapor application.
## Updating
The toolbox should be updated by the package manager it was installed with.
### Homebrew
```sh
brew upgrade vapor
```
### APT
```
sudo apt-get update
sudo apt-get install vapor
```
## Templates
The toolbox can create a project from the Vapor basic-template or any other git repo.
```sh
vapor new <name> [--template]
```
| Name | Flag | Description |
|------|----------------|-----------------------------------|
| API | --template=api | JSON API with Fluent database. |
| Web | --template=web | HTML website with Leaf templates. |
View a list of all [templates](https://github.com/search?utf8=✓&q=topic%3Avapor+topic%3Atemplate&type=Repositories) on GitHub.
!!! note
If you do not specify a template option, the API template will be used.
This may change in the future.
### Options
The toolbox will build an absolute URL based on what you pass as the template option.
- `--template=web` clones `http://github.com/vapor/web-template`
- `--template=user/repo` clones `http://github.com/user/repo`.
- `--template=http://example.com/repo-path` clones the full url given.
- `--branch=foo` can be used to specify a branch besides `master`.

Some files were not shown because too many files have changed in this diff Show More