From 6fe9511f84ea336bc02ac24292f69f7d130eef7e Mon Sep 17 00:00:00 2001 From: tanner0101 Date: Tue, 13 Mar 2018 21:11:16 -0400 Subject: [PATCH] deploy --- build/2.0/bits/overview/index.html | 1932 ---------- build/2.0/bits/package/index.html | 1890 ---------- build/2.0/core/overview/index.html | 1914 ---------- build/2.0/core/package/index.html | 1890 ---------- build/2.0/debugging/overview/index.html | 1837 --------- build/2.0/debugging/package/index.html | 1890 ---------- build/2.0/deploy/apache2/index.html | 2104 ----------- build/2.0/deploy/cloud/index.html | 1883 ---------- build/2.0/deploy/nginx/index.html | 2121 ----------- build/2.0/deploy/supervisor/index.html | 1955 ---------- build/2.0/http/client/index.html | 2027 ---------- build/2.0/http/cors/index.html | 1984 ---------- build/2.0/http/responder/index.html | 1956 ---------- .../http/response-representable/index.html | 1973 ---------- build/2.0/http/server/index.html | 2197 ----------- build/2.0/leaf/leaf/index.html | 2368 ------------ build/2.0/leaf/package/index.html | 1956 ---------- build/2.0/leaf/provider/index.html | 1938 ---------- build/2.0/node/getting-started/index.html | 2040 ---------- build/2.0/node/package/index.html | 1890 ---------- build/2.0/search/search_index.json | 3279 ----------------- build/2.0/validation/overview/index.html | 2008 ---------- build/2.0/validation/package/index.html | 1910 ---------- build/2.0/version/1_5/index.html | 1810 --------- build/2.0/version/2_0/index.html | 1810 --------- build/2.0/version/3_0/index.html | 1810 --------- build/2.0/version/support/index.html | 1932 ---------- 27 files changed, 54304 deletions(-) delete mode 100644 build/2.0/bits/overview/index.html delete mode 100644 build/2.0/bits/package/index.html delete mode 100644 build/2.0/core/overview/index.html delete mode 100644 build/2.0/core/package/index.html delete mode 100644 build/2.0/debugging/overview/index.html delete mode 100644 build/2.0/debugging/package/index.html delete mode 100644 build/2.0/deploy/apache2/index.html delete mode 100644 build/2.0/deploy/cloud/index.html delete mode 100644 build/2.0/deploy/nginx/index.html delete mode 100644 build/2.0/deploy/supervisor/index.html delete mode 100644 build/2.0/http/client/index.html delete mode 100644 build/2.0/http/cors/index.html delete mode 100644 build/2.0/http/responder/index.html delete mode 100644 build/2.0/http/response-representable/index.html delete mode 100644 build/2.0/http/server/index.html delete mode 100644 build/2.0/leaf/leaf/index.html delete mode 100644 build/2.0/leaf/package/index.html delete mode 100644 build/2.0/leaf/provider/index.html delete mode 100644 build/2.0/node/getting-started/index.html delete mode 100644 build/2.0/node/package/index.html delete mode 100644 build/2.0/search/search_index.json delete mode 100644 build/2.0/validation/overview/index.html delete mode 100644 build/2.0/validation/package/index.html delete mode 100644 build/2.0/version/1_5/index.html delete mode 100644 build/2.0/version/2_0/index.html delete mode 100644 build/2.0/version/3_0/index.html delete mode 100644 build/2.0/version/support/index.html diff --git a/build/2.0/bits/overview/index.html b/build/2.0/bits/overview/index.html deleted file mode 100644 index 2f097a60..00000000 --- a/build/2.0/bits/overview/index.html +++ /dev/null @@ -1,1932 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Overview - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

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.

-
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.

-
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.

-
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.

-
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.

-
let byte: Byte = 65
-if byte == .A {
-    print("found A!")
-}
-
- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/bits/package/index.html b/build/2.0/bits/package/index.html deleted file mode 100644 index 2ca2da33..00000000 --- a/build/2.0/bits/package/index.html +++ /dev/null @@ -1,1890 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Package - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Using Bits

-

With Vapor

-

This package is included with Vapor by default, just add:

-
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.

-
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.

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/core/overview/index.html b/build/2.0/core/overview/index.html deleted file mode 100644 index 97c78ef9..00000000 --- a/build/2.0/core/overview/index.html +++ /dev/null @@ -1,1914 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Overview - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Core

-

Core provides some conveniences for common tasks.

-

Background

-

Easily create a background thread using background()

-
print("hello")
-
-try background {
-    print("world")  
-}
-
- - -

Portal

-

Portals allow you to make async tasks blocking.

-
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.

-
let now = Date().rfc1123 // string 
-
- - -

You can also parse RFC1123 strings.

-
let parsed = Date(rfc1123: "Mon, 10 Apr 2017 11:26:13 GMT")
-
- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/core/package/index.html b/build/2.0/core/package/index.html deleted file mode 100644 index 54109080..00000000 --- a/build/2.0/core/package/index.html +++ /dev/null @@ -1,1890 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Package - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Using Core

-

With Vapor

-

This package is included with Vapor by default, just add:

-
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.

-
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.

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/debugging/overview/index.html b/build/2.0/debugging/overview/index.html deleted file mode 100644 index 71a42c89..00000000 --- a/build/2.0/debugging/overview/index.html +++ /dev/null @@ -1,1837 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Overview - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Debugging

-

Conforming your error types to Debuggable allows Vapor to create richer error messages and makes debugging easier.

-
import Debugging
-
-extension FooError: Debuggable {
-    // conform here
-}
-
- - -

Now when a FooError is thrown, you will get a nice message in your console.

-
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
-
- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/debugging/package/index.html b/build/2.0/debugging/package/index.html deleted file mode 100644 index ccf8545a..00000000 --- a/build/2.0/debugging/package/index.html +++ /dev/null @@ -1,1890 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Package - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Using Debugging

-

With Vapor

-

This package is included with Vapor by default, just add:

-
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.

-
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.

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/deploy/apache2/index.html b/build/2.0/deploy/apache2/index.html deleted file mode 100644 index 75b61279..00000000 --- a/build/2.0/deploy/apache2/index.html +++ /dev/null @@ -1,2104 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Apache2 - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Deploying with Apache2

-

Apache2 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.

-
-

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

-

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:

- -

APT

-

Apache2 can be installed through APT.

-
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

-
http://server_domain_name_or_IP
-
- - -

Service

-

The service can be started or stopped.

-
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 and Nginx 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.

-
# 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.

-
<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.

-
<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.

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/deploy/cloud/index.html b/build/2.0/deploy/cloud/index.html deleted file mode 100644 index d5f8bd7a..00000000 --- a/build/2.0/deploy/cloud/index.html +++ /dev/null @@ -1,1883 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Cloud - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Vapor Cloud

-

The best way to deploy your Vapor application is Vapor Cloud.

-

Vapor Cloud Website

-

Quick Start

-

If you already have the Vapor Toolbox installed, then you can deploy your -Vapor app to the cloud with just one command.

-
vapor cloud deploy
-
- - -

Vapor Toolbox Deploy

-

!!! 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 on Vapor Cloud's docs for detailed -instructions on how to deploy your app to Vapor Cloud!

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/deploy/nginx/index.html b/build/2.0/deploy/nginx/index.html deleted file mode 100644 index e93fa413..00000000 --- a/build/2.0/deploy/nginx/index.html +++ /dev/null @@ -1,2121 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Nginx - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

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

-

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:

- -

APT

-

Nginx can be installed through APT.

-
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

-
http://server_domain_name_or_IP
-
- - -

Service

-

The service can be started or stopped.

-
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 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.

-
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.

-
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.

-
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.

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/deploy/supervisor/index.html b/build/2.0/deploy/supervisor/index.html deleted file mode 100644 index 86bc972d..00000000 --- a/build/2.0/deploy/supervisor/index.html +++ /dev/null @@ -1,1955 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Supervisor - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Supervisor

-

Supervisor is a process control system that makes it easy to start, stop, and restart your Vapor app.

-

Install

-
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

-
[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.

-
environment=PORT=8123
-
- - -

Exported variables can be used in Vapor's configuration files with the $ prefix.

-

Config/production/servers.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.

-
supervisorctl reread
-supervisorctl add hello
-supervisorctl start hello
-
- - -
-

Note

-

The add command may have already started your app.

-
- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/http/client/index.html b/build/2.0/http/client/index.html deleted file mode 100644 index 5e64ad8c..00000000 --- a/build/2.0/http/client/index.html +++ /dev/null @@ -1,2027 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Client - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

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.

-
let query = "..."
-let res = try drop.client.get("https://api.spotify.com/v1/search?type=artist&q=\(query)")
-print(res)
-
- - -

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:

-
let res = 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

-

Headers

-

You can also add additional headers to the request.

-
try drop.client.get("http://some-endpoint/json", headers: [
-    "API-Key": "vapor123"
-])
-
- - -

Custom Request

-

You can ask the client to respond to any Request that you create. -This is useful if you need to add JSON or FormURLEncoded data to the request.

-
let req = Request(method: .post, uri: "http://some-endpoint")
-req.formURLEncoded = Node(node: [
-    "email": "mymail@vapor.codes"
-])
-
-try drop.client.respond(to: req)
-
- - -

Re-usable Connection

-

Up to this point, we've been using drop.client which is a ClientFactory. This creates a new client and TCP connection for each request.

-

For more better performance, you can create an re-use a single client.

-
let pokemonClient = try drop.client.makeClient(
-    scheme: "http", 
-    host: "pokeapi.co",
-    securityLayer: .none
-)
-
-for i in 0...1 {
-    let response = try pokemonClient.get("/api/v2/pokemon/", query: [
-        "limit": 20, 
-        "offset": i
-    ])
-    print("response: \(response)")
-}
-
- - -
-

Note

-

Clients created using .makeClient can not connect to a different server after initialization. (Proxy servers are an exception)

-
-

Proxy

-

The drop.client can be configured to use a proxy by default.

-

Config/client.json

-
{
-    "proxy": {
-        "hostname": "google.com", 
-        "port": 80,
-        "securityLayer": "none"
-    }
-}
-
- - -

For the above example, all requests sent to drop.client.get(...) would be proxied through google.com.

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/http/cors/index.html b/build/2.0/http/cors/index.html deleted file mode 100644 index d3cf39d1..00000000 --- a/build/2.0/http/cors/index.html +++ /dev/null @@ -1,1984 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CORS - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

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/

-

To learn more about middlewares, please visit the Middleware section of the documentation here.

-

-Image Author: Wikipedia

-

Basic

-

First of all, add the CORS middleware into your droplet middlewares array.

-

Config/droplet.json

-
{
-    ...,
-    "middleware": [
-        ...,
-        "cors",
-        ...,
-    ],
-    ...,
-}
-
- - -

Next time you boot your application, you will be prompted to add a Config/cors.json file.

-

Config/cors.json

-
{
-    "allowedOrigin": "*",
-    "allowedMethods": ["GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH"],
-    "allowedHeaders": [
-       "Accept",
-       "Authorization",
-       "Content-Type",
-       "Origin",
-       "X-Requested-With"
-    ]
-}
-
- - -
-

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:

-
let config = try Config()
-config.addConfigurable(middleware: { config in
-    return CORSConfiguration(
-        allowedOrigin: .custom("https://vapor.codes"),
-        allowedMethods: [.get, .post, .options],
-        allowedHeaders: ["Accept", "Authorization"],
-        allowCredentials: false,
-        cacheExpiration: 600,
-        exposedHeaders: ["Cache-Control", "Content-Language"]
-    )
-}, name: "custom-cors")
-
- - -

Then set the custom-cors in your Droplet's middleware array.

-

Config/droplet.json

-
{
-    ...,
-    "middleware": [
-        ...,
-        "custom-cors",
-        ...,
-    ],
-    ...,
-}
-
- - -
-

Note: Please consult the documentation in the source code of the CORSConfiguration for more information about available values for the settings.

-
- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/http/responder/index.html b/build/2.0/http/responder/index.html deleted file mode 100644 index 3bd75376..00000000 --- a/build/2.0/http/responder/index.html +++ /dev/null @@ -1,1956 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Responder - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

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:

-
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:

-
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:

-
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:

-
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.

-
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.

-
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.

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/http/response-representable/index.html b/build/2.0/http/response-representable/index.html deleted file mode 100644 index 31705b08..00000000 --- a/build/2.0/http/response-representable/index.html +++ /dev/null @@ -1,1973 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ResponseRepresentable - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

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

-
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.

-
drop.get("hello") { request in
-    return "Hello, World!"
-}
-
- - -

JSON

-

JSON can be returned directly instead of recreating a response each time.

-
drop.get("hello") { request in
-    var json = JSON()
-    try json.set("hello", "world")
-    try json.set("some-numbers", [1, 2, 3])
-    return json
-}
-
- - -

Response

-

Of course, we can also return Responses for anything not covered:

-
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:

-
import Foundation
-
-struct BlogPost {
-  let id: String
-  let content: String
-  let createdAt: NSDate
-}
-
- - -

And now, let's conform it to response representable.

-
import HTTP
-
-extension BlogPost: ResponseRepresentable {
-    func makeResponse() throws -> Response {
-        var json = JSON()
-        try json.set("id", id)
-        try json.set("content", content)
-        try json.set("created-at", createdAt.timeIntervalSince1970)
-        return try json.makeResponse()
-    }
-}
-
- - -

Now that we've modeled our BlogPost, we can return it directly in route handlers.

-
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
-}
-
- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/http/server/index.html b/build/2.0/http/server/index.html deleted file mode 100644 index 9c724d14..00000000 --- a/build/2.0/http/server/index.html +++ /dev/null @@ -1,2197 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Server - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

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.

-
import Vapor
-
-let drop = try Droplet()
-try drop.run()
-
- - -

The default server will bind to host 0.0.0.0 at port 8080.

-

Config

-

If you are using a Config/server.json file, this is where you can easily change your host and port.

-
{
-    "port": "$PORT:8080",
-    "host": "0.0.0.0",
-    "securityLayer": "none"
-}
-
- - -

The default server.json is above. The port with try to resolve the environment variable $PORT or fallback to 8080.

-

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.

-
-
"tls": {
-    "verifyHost": false,
-    "verifyCertificates": false
-}
-
- - -

Certificates

-

None

-
"tls": {
-    "certificates": "none"
-}
-
- - -

Chain

-
"tls": {
-    "certificates": "chain",
-    "chainFile": "/path/to/chainfile"
-}
-
- - -

Files

-
"tls": {
-    "certificates": "files",
-    "certificateFile": "/path/to/cert.pem",
-    "privateKeyFile": "/path/to/key.pem"
-}
-
- - -

Certificate Authority

-
"tls": {
-    "certificates": "ca"
-}
-
- - -

Signature

-

Self Signed

-
"tls": {
-    "signature": "selfSigned"
-}
-
- - -

Signed File

-
"tls": {
-    "signature": "signedFile",
-    "caCertificateFile": "/path/to/file"
-}
-
- - -

Signed Directory

-
"tls": {
-    "signature": "signedDirectory",
-    "caCertificateDirectory": "/path/to/dir"
-}
-
- - -

Example

-

Here is an example server.json file using certificate files with a self signed signature and host verification redundantly set to true.

-
{
-    "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"
-    }
-}
-
- - -

Nginx

-

It is highly recommended that you serve your Vapor project behind Nginx in production. Read more in the deploy Nginx section.

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/leaf/leaf/index.html b/build/2.0/leaf/leaf/index.html deleted file mode 100644 index 726c308b..00000000 --- a/build/2.0/leaf/leaf/index.html +++ /dev/null @@ -1,2368 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Overview - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - - - - -
-
- - - - - -
-

Warning

-

This section may contain outdated information.

-
-

Leaf

-

Welcome to Leaf. Leaf's goal is to be a simple templating language that can make generating views easier. There are plenty of great templating languages, so use what's best for you -- maybe that's Leaf! The goals of Leaf are:

-
    -
  • 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(array, "item") {}

-
#loop(friends, "friend") {
-  #(offset). #(friend.name)
-}
-
- - -

The body of a #loop can access an index variable corresponding to the index of the array. There is also an offset variable which is the index plus 1.

-

Index: #index(array, _ index: String)

-
Hello, #index(friends, "0")!
-Hello, #index(friends, "best")!
-
- - -

Note: array indexes are always strings.

-

If - Else: #if(bool) ##else() { this }

-
#if(entering) {
-  Hello, there!
-} ##if(leaving) {
-  Goodbye!
-} ##else() {
-  I've been here the whole time.
-}
-
- - -

Note that #if requires a boolean variable. If you need to do a comparison then #equal is more appropriate. You can chain #equal in the same way as #if:

-
#equal(state, "0") {
-  <span class="green">Normal</span>
-} ##equal(state, "1") {
-  <span class="orange">Warning</span>
-} ##else() {
-  <span class="red">Alert</span>
-}
-
- - -

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.

-
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:

-
if let leaf = drop.view as? LeafRenderer {
-    leaf.stem.register(Index())
-}
-
- - -

And use it just like we did above.

-
-

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 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 but that requires a bit more kung-fu.

-

VS Code

-

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

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/leaf/package/index.html b/build/2.0/leaf/package/index.html deleted file mode 100644 index 3b374c73..00000000 --- a/build/2.0/leaf/package/index.html +++ /dev/null @@ -1,1956 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Package - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Using Leaf

-

This section outlines how to import the Leaf package both with or without a Vapor project.

-

Example Folder Structure

-
Hello
-├── Config
-│   ├── app.json
-│   ├── crypto.json
-│   ├── droplet.json
-│   ├── fluent.json
-│   └── server.json
-├── Package.pins
-├── Package.swift
-├── Public
-├── README.md
-├── Resources
-│   ├── Views
-│   │   │   └── hello.leaf
-├── Public
-│   ├── images (images resources)
-│   ├── styles (css resources) 
-├── Sources
-│   ├── App
-│   │   ├── Config+Setup.swift
-│   │   ├── Controllers
-│   │   │   └── PostController.swift
-│   │   ├── Droplet+Setup.swift
-│   │   ├── Models
-│   │   │   └── Post.swift
-│   │   └── Routes.swift
-│   └── Run
-│       └── main.swift
-├── Tests
-│   ├── AppTests
-│   │   ├── PostControllerTests.swift
-│   │   ├── RouteTests.swift
-│   │   └── Utilities.swift
-│   └── LinuxMain.swift
-├── circle.yml
-└── license
-
- - -

With Vapor

-

The easiest way to use Leaf with Vapor is to include the Leaf provider.

-
import PackageDescription
-
-let package = Package(
-    name: "Project",
-    dependencies: [
-        .Package(url: "https://github.com/vapor/vapor.git", majorVersion: 2),
-        .Package(url: "https://github.com/vapor/leaf-provider.git", majorVersion: 1)
-    ],
-    exclude: [ ... ]
-)
-
- - -

The Leaf provider package adds Leaf to your project and adds some additional, Vapor-specific conveniences like drop.stem().

-

Use import LeafProvider.

-

Just Leaf

-

At the core of the Leaf provider is a fast, pure Swift templating engine. You can use it with any of your Swift packages or server-side Swift applications.

-
import PackageDescription
-
-let package = Package(
-    name: "Project",
-    dependencies: [
-        ...
-        .Package(url: "https://github.com/vapor/leaf.git", majorVersion: 2)
-    ],
-    exclude: [ ... ]
-)
-
- - -

Use import Leaf to access the Leaf.Stem class.

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/leaf/provider/index.html b/build/2.0/leaf/provider/index.html deleted file mode 100644 index 166a72c0..00000000 --- a/build/2.0/leaf/provider/index.html +++ /dev/null @@ -1,1938 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Provider - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Leaf Provider

-

After you've added the Leaf Provider package to your project, setting the provider up in code is easy.

-

Add to Droplet

-

First, register the LeafProvider.Provider with your Droplet.

-
import Vapor
-import LeafProvider
-
-let config = try Config()
-try config.addProvider(LeafProvider.Provider.self)
-
-let drop = try Droplet(config)
-
-...
-
- - -

Configure Droplet

-

Once the provider is added to your Droplet, you can configure your Droplet to use the Leaf view renderer.

-

Config/droplet.json

-
{
-    ...,
-    "view": "leaf",
-    ...
-}
-
- - -
-

Seealso

-

Learn more about configuration files in the Settings guide.

-
-

Manual

-

You can also set the drop.view property manually if you want to hardcode your view renderer.

-
import Vapor
-import LeafProvider
-
-let view = LeafRenderer(viewsDir: drop.viewsDir)
-let drop = try Droplet(view: view)
-
- - -

Done

-

Next time you boot your application, your views will be rendered using Leaf.

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/node/getting-started/index.html b/build/2.0/node/getting-started/index.html deleted file mode 100644 index c79bacce..00000000 --- a/build/2.0/node/getting-started/index.html +++ /dev/null @@ -1,2040 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Getting Started - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Getting Started

-

Why do we have Node?

-

The web is very stringy, Swift is very type-safe, this is a major problem when doing web development in Swift. Node is our attempt at providing a solution to this problem.

-

What is Node?

-

Node is a data abstraction with an emphasis on being an intermediary between distinct types. For example, json from a client might use node to convert between the JSON and itself.

-

How do I use it?

-

Node can be a little different to work with at first if you're familiar with less type-safe languages, let's look at a couple of examples and how we might start using Node in our projects. Most often, we'll be working with Node conversions.

-

NodeInitializable

-

NodeInitializable can be read and understood as An object that can be initialized with a Node. Let's look at a simple implementation.

-
struct Person: NodeInitializable {
-    let name: String
-    let age: Int
-
-    init(node: Node) throws {
-        name = try node.get("name")
-        age = try node.get("age")
-    }
-}
-
- - -

Now that we have this, we can easily convert abstract data to a Person. Here's how that might look:

-
let person = try Person(node: json)
-
- - -
-

Note: There are some more advanced functionality options for JSON and database Row types in particular, we'll cover that later.

-
-

By conforming our Person object to NodeInitializable, we can also use more advanced cases such as arrays:

-
let people = try [Person](node: jsonArray)
-
- - -

NodeRepresentable

-

NodeRepresentable can be read and understood as An object that can be represented as a Node. Let's take a look at a simple implementation. We'll stick with the Person example above

-
extension Person: NodeRepresentable {
-    func makeNode(in context: Context) throws -> Node {
-        var node = Node(context)
-        try node.set("name", name)
-        try node.set("age", age)
-        return node
-    }
-}
-
- - -

Now that we've done this, we can easily convert our person or a collection of Person objects into a Node.

-
let node = try person.makeNode(in: nil)
-
- - -

And also for collections, like arrays

-
let node = try [kim, joe, jan].makeNode(in: nil)
-
- - -

Context

-

Up to this point, we've seen Context a lot, but what's it for. When we're serializing or mapping an object, we might have a lot of different situations we're mapping differently for. Maybe one is for the database, one is for the view, one is for JSON, etc.

-

If you're using Vapor, we provide a lot of contexts and more native integration options, but here's how one might define their own.

-
import Node
-
-final class MyContext: Context {
-}
-
-let myContext = MyContext()
-
-extension Context {
-    var isMyContext: Bool {
-        return self is MyContext
-    }
-}
-
- - -

Now inside our object, we could add special behavior.

-
extension Person: NodeRepresentable {
-    func makeNode(in context: Context) throws -> Node {
-        var node = Node(context)
-        try node.set("name", name)
-        try node.set("age", age)
-        if context.isMyContext {
-            try node.set("special-attribute", special)
-        }
-        return node
-    }
-}
-
- - -

We might call it like this:

-
let specialNode = person.makeNode(in: myContext)
-
- - -

This is a common usage, but can be adapted for any scenario where we require special metadata to help us properly serialize or map our object.

-

NodeConvertible

-

NodeConvertible is simply the combination of Representable and Initializable. These objects can be converted easily to and from node. Taking our Person object from earlier, we should be able to do this:

-
// ..
-let node = person.makeNode(in: myContext)
-let back = try Person(node: node)
-print("\(person) went to node and back to \(back)")
-
- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/node/package/index.html b/build/2.0/node/package/index.html deleted file mode 100644 index d487e99f..00000000 --- a/build/2.0/node/package/index.html +++ /dev/null @@ -1,1890 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Package - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Using Node

-

With Vapor

-

This package is included with Vapor by default, just add:

-
import Node
-
- - -

Without Vapor

-

Node provides a lot of conveniences for any server-side, or client side Swift project. To include it in your package, add the following to your Package.swift file.

-
import PackageDescription
-
-let package = Package(
-    name: "Project",
-    dependencies: [
-        ...
-        .Package(url: "https://github.com/vapor/node.git", majorVersion: 2)
-    ],
-    exclude: [ ... ]
-)
-
- - -

Use import Node to access Node's APIs

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/search/search_index.json b/build/2.0/search/search_index.json deleted file mode 100644 index f124a034..00000000 --- a/build/2.0/search/search_index.json +++ /dev/null @@ -1,3279 +0,0 @@ -{ - "docs": [ - { - "location": "/", - "text": "Vapor Documentation\n\n\nThis 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.\n\n\nVapor is the most used web framework for Swift. It provides a beautifully expressive and easy to use foundation for your next website or API.\n\n\nGetting Started\n\n\nIf this is your first time using Vapor, head to the \nGetting Started\n section to install Swift and create your first app.\n\n\nLike Vapor?\n\n\nOur small team works hard to make Vapor awesome (and free). Support the framework by starring Vapor on GitHub or donating $1 monthly--it helps us a lot. Thanks!\n\n\n\n \n\n\n\n\n\n \n\n\n\n\nOther Sources\n\n\nHere are some other great places to find information about Vapor.\n\n\nAPI\n\n\nAuto-generated API documentation is located at \napi.vapor.codes\n.\n\n\nStack Overflow\n\n\nView or ask questions related to Vapor on Stack Overflow using the \nvapor\n tag.\n\n\nGitHub\n\n\nSource Code\n\n\nTo view the framework's source code and code documentation, visit \nVapor's GitHub\n.\n\n\nIssues\n\n\nTo view open bug reports and feature requests, or to create one, visit the \nissues\n tab on \nVapor's GitHub\n.\n\n\nPackages\n\n\nVapor is a modular framework built for a modular language. Code is split up into modules which are grouped to form packages. Packages can be added to your project by adding the package's Git url to your \nPackage.swift\n file. Once a package is included, all of its modules will be available to \nimport\n. You can read more about packages and modules in the Swift Package Manager \nconceptual overview\n.\n\n\nBelow is a list of packages and modules that come with or can be used by Vapor projects. Packages will have a link to their respective GitHub page.\n\n\nCore\n\n\nCore packages are maintained by the Vapor team.\n\n\nIncluded\n\n\nThe following packages are included with Vapor by default.\n\n\n\n\nTip\n\n\nThese packages can also be used individually\n\n\n\n\n\n\nVapor\n: Swift's most used web framework.\n\n\nAuth: User authentication and persistance.\n\n\nSessions: Secure, ephemeral cookie based data storage.\n\n\nCookies: HTTP cookies.\n\n\nRouting: Advanced router with type-safe parameterization.\n\n\n\n\n\n\nEngine\n: Core transport layers.\n\n\nHTTP: HTTP client and server.\n\n\nURI: URI parsing and serializing.\n\n\nWebSockets: Full-duplex communication channels over a single TCP connection.\n\n\nSMTP: Send email using Sendgrid and Gmail.\n\n\n\n\n\n\nMultipart\n: Fast, streaming, non-blocking multipart parser and serializer.\n\n\nMultipart: Parses and serializes \nmultipart/mixed\n.\n\n\nFormData: Parses and serializes \nmultipart/form-data\n.\n\n\n\n\n\n\nJSON\n: Conveniences for working with JSON in Swift.\n\n\nConsole\n: Swift wrapper for console IO and commands.\n\n\nTLS\n: Swift wrapper for CLibreSSL's new TLS.\n\n\nCrypto\n: Cryptography from LibreSSL and Swift.\n\n\nDigests: Hashing with and without authentication.\n\n\nCiphers: Encryption and decryption\n\n\nRandom: Pseudo and cryptographically secure randomness.\n\n\nBCrypt: Pure Swift implementation.\n\n\n\n\n\n\nNode\n: Data structure for easy type conversions.\n\n\nPolymorphic\n: Syntax for easily accessing values from common types like JSON.\n\n\nPath Indexable\n: A protocol for powerful subscript access of common types like JSON.\n\n\n\n\n\n\nCore\n: Core extensions, type-aliases, and functions that facilitate common tasks.\n\n\nSocks\n: Swift C Socket API wrapper.\n\n\nBits\n: Low level byte manipulation helpers\n\n\n\n\nProviders\n\n\nThese are officially supported packages for Vapor that are not included by default.\n\n\n\n\nFluent\n: Models, relationships, and querying for NoSQL and SQL databases.\n\n\nFluent Provider\n: Fluent provider for Vapor.\n\n\n\n\n\n\nMySQL\n: Robust MySQL interface for Swift.\n\n\nMySQL Driver\n: MySQL driver for Fluent.\n\n\nMySQL Provider\n: MySQL provider for Vapor.\n\n\n\n\n\n\nLeaf\n: An extensible templating language.\n\n\nLeaf Provider\n: Leaf provider for Vapor.\n\n\n\n\n\n\nRedis\n: Pure-Swift Redis client implemented from the original protocol spec.\n\n\nRedis Provider\n: Redis cache provider for Vapor.\n\n\n\n\n\n\nJWT\n: JSON Web Tokens in Swift.\n\n\nJWT Provider\n: JWT conveniences for Vapor.\n\n\n\n\n\n\n\n\nCommunity\n\n\nThese are packages maintained by community members that work great with Vapor.\n\n\n\n\nAPNS\n: Simple APNS Library for Vapor (Swift).\n\n\nFlock\n: Automated deployment of Swift projects to servers\n\n\nVaporFlock\n: Use Flock to deploy Vapor applications\n\n\n\n\n\n\nHeimdall\n: An easy to use HTTP request logger.\n\n\nJobs\n: A minimalistic job/background-task system for Swift.\n\n\nKitura Provider\n: Use IBM's Kitura HTTP server in Vapor.\n\n\nLeaf Error Middleware\n - Custom 404 and error views for your website\n\n\nMarkdownProvider\n - Easily use Markdown from Leaf.\n\n\nMongoKitten\n: Native MongoDB driver for Swift, written in Swift\n\n\nMongo Driver\n: MongoKitten driver for Fluent.\n\n\nMongo Provider\n: MongoKitten provider for Vapor.\n\n\n\n\n\n\nPostgreSQL\n: Robust PostgreSQL interface for Swift.\n\n\nPostgreSQL Driver\n: PostgreSQL driver for Fluent.\n\n\nPostgreSQL Provider\n: PostgreSQL provider for Vapor.\n\n\n\n\n\n\nSanitize\n: Powerful model extraction from JSON requests.\n\n\nSteamPress\n: A blogging engine for Vapor.\n\n\nSwiftyBeaver\n: Adds the powerful logging of SwiftyBeaver to Vapor.\n\n\nVaporFCM\n: Simple FCM (iOS + Android Push Notifications) library built for Vapor in Swift.\n\n\nVaporForms\n: Brings simple, dynamic and re-usable web form handling to Vapor.\n\n\nVaporS3Signer\n: Generate V4 Auth Header/Pre-Signed URL for AWS S3 REST API\n\n\nVapor OAuth\n - An OAuth2 Provider library for Vapor\n\n\nVapor Security Headers\n: Add common security headers to your Vapor Application.\n\n\n\n\nProviders\n\n\nVapor providers are a convenient way to add functionality to your Vapor projects. For a full list of providers, check out the \nvapor-provider\n tag on GitHub.\n\n\nAuthors\n\n\nTanner Nelson\n, \nLogan Wright\n, and the hundreds of members of Vapor.", - "title": "Overview" - }, - { - "location": "/#vapor-documentation", - "text": "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.", - "title": "Vapor Documentation" - }, - { - "location": "/#getting-started", - "text": "If this is your first time using Vapor, head to the Getting Started section to install Swift and create your first app.", - "title": "Getting Started" - }, - { - "location": "/#like-vapor", - "text": "Our small team works hard to make Vapor awesome (and free). Support the framework by starring Vapor on GitHub or donating $1 monthly--it helps us a lot. Thanks!", - "title": "Like Vapor?" - }, - { - "location": "/#other-sources", - "text": "Here are some other great places to find information about Vapor.", - "title": "Other Sources" - }, - { - "location": "/#api", - "text": "Auto-generated API documentation is located at api.vapor.codes .", - "title": "API" - }, - { - "location": "/#stack-overflow", - "text": "View or ask questions related to Vapor on Stack Overflow using the vapor tag.", - "title": "Stack Overflow" - }, - { - "location": "/#github", - "text": "", - "title": "GitHub" - }, - { - "location": "/#source-code", - "text": "To view the framework's source code and code documentation, visit Vapor's GitHub .", - "title": "Source Code" - }, - { - "location": "/#issues", - "text": "To view open bug reports and feature requests, or to create one, visit the issues tab on Vapor's GitHub .", - "title": "Issues" - }, - { - "location": "/#packages", - "text": "Vapor is a modular framework built for a modular language. Code is split up into modules which are grouped to form packages. Packages can be added to your project by adding the package's Git url to your Package.swift file. Once a package is included, all of its modules will be available to import . You can read more about packages and modules in the Swift Package Manager conceptual overview . Below is a list of packages and modules that come with or can be used by Vapor projects. Packages will have a link to their respective GitHub page.", - "title": "Packages" - }, - { - "location": "/#core", - "text": "Core packages are maintained by the Vapor team.", - "title": "Core" - }, - { - "location": "/#included", - "text": "The following packages are included with Vapor by default. Tip These packages can also be used individually 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. Engine : Core transport layers. HTTP: HTTP client and server. URI: URI parsing and serializing. WebSockets: Full-duplex communication channels over a single TCP connection. SMTP: Send email using Sendgrid and Gmail. Multipart : Fast, streaming, non-blocking multipart parser and serializer. Multipart: Parses and serializes multipart/mixed . FormData: Parses and serializes multipart/form-data . JSON : Conveniences for working with JSON in Swift. Console : Swift wrapper for console IO and commands. TLS : Swift wrapper for CLibreSSL's new TLS. 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 : Data structure for easy type conversions. Polymorphic : Syntax for easily accessing values from common types like JSON. Path Indexable : A protocol for powerful subscript access of common types like JSON. Core : Core extensions, type-aliases, and functions that facilitate common tasks. Socks : Swift C Socket API wrapper. Bits : Low level byte manipulation helpers", - "title": "Included" - }, - { - "location": "/#providers", - "text": "These are officially supported packages for Vapor that are not included by default. Fluent : Models, relationships, and querying for NoSQL and SQL databases. Fluent Provider : Fluent provider for Vapor. MySQL : Robust MySQL interface for Swift. MySQL Driver : MySQL driver for Fluent. MySQL Provider : MySQL provider for Vapor. Leaf : An extensible templating language. Leaf Provider : Leaf provider for Vapor. Redis : Pure-Swift Redis client implemented from the original protocol spec. Redis Provider : Redis cache provider for Vapor. JWT : JSON Web Tokens in Swift. JWT Provider : JWT conveniences for Vapor.", - "title": "Providers" - }, - { - "location": "/#community", - "text": "These are packages maintained by community members that work great with Vapor. APNS : Simple APNS Library for Vapor (Swift). Flock : Automated deployment of Swift projects to servers VaporFlock : Use Flock to deploy Vapor applications Heimdall : An easy to use HTTP request logger. Jobs : A minimalistic job/background-task system for Swift. Kitura Provider : Use IBM's Kitura HTTP server in Vapor. Leaf Error Middleware - Custom 404 and error views for your website MarkdownProvider - Easily use Markdown from Leaf. MongoKitten : Native MongoDB driver for Swift, written in Swift Mongo Driver : MongoKitten driver for Fluent. Mongo Provider : MongoKitten provider for Vapor. PostgreSQL : Robust PostgreSQL interface for Swift. PostgreSQL Driver : PostgreSQL driver for Fluent. PostgreSQL Provider : PostgreSQL provider for Vapor. Sanitize : Powerful model extraction from JSON requests. SteamPress : A blogging engine for Vapor. SwiftyBeaver : Adds the powerful logging of SwiftyBeaver to Vapor. VaporFCM : Simple FCM (iOS + Android Push Notifications) library built for Vapor in Swift. VaporForms : Brings simple, dynamic and re-usable web form handling to Vapor. VaporS3Signer : Generate V4 Auth Header/Pre-Signed URL for AWS S3 REST API Vapor OAuth - An OAuth2 Provider library for Vapor Vapor Security Headers : Add common security headers to your Vapor Application.", - "title": "Community" - }, - { - "location": "/#providers_1", - "text": "Vapor providers are a convenient way to add functionality to your Vapor projects. For a full list of providers, check out the vapor-provider tag on GitHub.", - "title": "Providers" - }, - { - "location": "/#authors", - "text": "Tanner Nelson , Logan Wright , and the hundreds of members of Vapor.", - "title": "Authors" - }, - { - "location": "/getting-started/install-on-macos/", - "text": "Install on macOS\n\n\nTo use Vapor on macOS, you just need to have Xcode 8 or later installed.\n\n\nInstall Xcode\n\n\nInstall \nXcode 9\n from the Mac App Store.\n\n\n\n\n(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 \ndownloads page\n)\n\n\nOpen Xcode\n\n\nAfter Xcode has been downloaded, you must open it to finish the installation. This may take a while.\n\n\nVerify Swift Installation\n\n\nDouble check the installation was successful by opening Terminal and running:\n\n\neval\n \n$(\ncurl -sL check.vapor.sh\n)\n\n\n\n\n\n\nInstall Vapor\n\n\nNow that you have Swift 4 (or Swift 3.1 if you installed Xcode 8), let's install the Vapor toolbox. \n\n\nThe toolbox includes all of Vapor's dependencies as well as a handy CLI tool for creating new projects.\n\n\nInstall Homebrew\n\n\nIf 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.\n\n\n/usr/bin/ruby -e \n$(\ncurl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install\n)\n\n\n\n\n\n\nFor more information on installing Homebrew, visit \nbrew.sh\n.\n\n\nAdd Homebrew Tap\n\n\nVapor's Homebrew tap will give your Homebrew installation access to all of Vapor's macOS packages.\n\n\nbrew tap vapor/homebrew-tap\nbrew update\n\n\n\n\n\nInstall\n\n\nNow that you've added Vapor's tap, you can install Vapor's toolbox and dependencies.\n\n\nbrew install vapor\n\n\n\n\n\nUpgrade\n\n\nIf 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.\n\n\nbrew update\nbrew upgrade vapor\n\n\n\n\n\nNext\n\n\nLearn more about the Vapor toolbox CLI in the \nToolbox section\n of the Getting Started section.\n\n\nSwift.org\n\n\nCheck out \nSwift.org\n's extensive guides if you need more detailed instructions for installing Swift.", - "title": "Install: macOS" - }, - { - "location": "/getting-started/install-on-macos/#install-on-macos", - "text": "To use Vapor on macOS, you just need to have Xcode 8 or later installed.", - "title": "Install on macOS" - }, - { - "location": "/getting-started/install-on-macos/#install-xcode", - "text": "Install Xcode 9 from the Mac App Store. (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 )", - "title": "Install Xcode" - }, - { - "location": "/getting-started/install-on-macos/#open-xcode", - "text": "After Xcode has been downloaded, you must open it to finish the installation. This may take a while.", - "title": "Open Xcode" - }, - { - "location": "/getting-started/install-on-macos/#verify-swift-installation", - "text": "Double check the installation was successful by opening Terminal and running: eval $( curl -sL check.vapor.sh )", - "title": "Verify Swift Installation" - }, - { - "location": "/getting-started/install-on-macos/#install-vapor", - "text": "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.", - "title": "Install Vapor" - }, - { - "location": "/getting-started/install-on-macos/#install-homebrew", - "text": "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. /usr/bin/ruby -e $( curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install ) For more information on installing Homebrew, visit brew.sh .", - "title": "Install Homebrew" - }, - { - "location": "/getting-started/install-on-macos/#add-homebrew-tap", - "text": "Vapor's Homebrew tap will give your Homebrew installation access to all of Vapor's macOS packages. brew tap vapor/homebrew-tap\nbrew update", - "title": "Add Homebrew Tap" - }, - { - "location": "/getting-started/install-on-macos/#install", - "text": "Now that you've added Vapor's tap, you can install Vapor's toolbox and dependencies. brew install vapor", - "title": "Install" - }, - { - "location": "/getting-started/install-on-macos/#upgrade", - "text": "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. brew update\nbrew upgrade vapor", - "title": "Upgrade" - }, - { - "location": "/getting-started/install-on-macos/#next", - "text": "Learn more about the Vapor toolbox CLI in the Toolbox section of the Getting Started section.", - "title": "Next" - }, - { - "location": "/getting-started/install-on-macos/#swiftorg", - "text": "Check out Swift.org 's extensive guides if you need more detailed instructions for installing Swift.", - "title": "Swift.org" - }, - { - "location": "/getting-started/install-on-ubuntu/", - "text": "Install on Ubuntu\n\n\nInstalling Vapor on Ubuntu only takes a couple of minutes. \n\n\nSupported\n\n\nVapor supports the same versions of Ubuntu that Swift supports.\n\n\n\n\n\n\n\n\nVersion\n\n\nCodename\n\n\n\n\n\n\n\n\n\n\n16.10\n\n\nYakkety Yak\n\n\n\n\n\n\n16.04\n\n\nXenial Xerus\n\n\n\n\n\n\n14.04\n\n\nTrusty Tahr\n\n\n\n\n\n\n\n\nAPT Repo\n\n\nAdd Vapor's APT repo to get access to all of Vapor's system packages.\n\n\nQuick Script\n\n\nEasily add Vapor's APT repo with this handy script.\n\n\neval\n \n$(\ncurl -sL https://apt.vapor.sh\n)\n\n\n\n\n\n\n\n\nNote\n\n\nThis command requires \ncurl\n which can be installed using \nsudo apt-get install curl\n\n\n\n\nDockerfile\n\n\nWhen configuring Ubuntu from a Dockerfile, adding the APT repo can be done via this command:\n\n\n RUN /bin/bash -c \n$(\nwget -qO- https://apt.vapor.sh\n)\n\n\n\n\n\n\nManual\n\n\nOr add the repo manually.\n\n\nwget -q https://repo.vapor.codes/apt/keyring.gpg -O- \n|\n sudo apt-key add -\n\necho\n \ndeb https://repo.vapor.codes/apt \n$(\nlsb_release -sc\n)\n main\n \n|\n sudo tee /etc/apt/sources.list.d/vapor.list\nsudo apt-get update\n\n\n\n\n\nInstall Vapor\n\n\nNow that you have added Vapor's APT repo, you can install the required dependencies.\n\n\nsudo apt-get install swift vapor\n\n\n\n\n\nVerify Installation\n\n\nDouble check the installation was successful by running:\n\n\neval\n \n$(\ncurl -sL check.vapor.sh\n)\n\n\n\n\n\n\nNext\n\n\nLearn more about the Vapor toolbox CLI in the \nToolbox section\n of the Getting Started section.\n\n\nSwift.org\n\n\nCheck out \nSwift.org\n's guide to \nusing downloads\n if you need more detailed instructions for installing Swift 3.1.", - "title": "Install: Ubuntu" - }, - { - "location": "/getting-started/install-on-ubuntu/#install-on-ubuntu", - "text": "Installing Vapor on Ubuntu only takes a couple of minutes.", - "title": "Install on Ubuntu" - }, - { - "location": "/getting-started/install-on-ubuntu/#supported", - "text": "Vapor supports the same versions of Ubuntu that Swift supports. Version Codename 16.10 Yakkety Yak 16.04 Xenial Xerus 14.04 Trusty Tahr", - "title": "Supported" - }, - { - "location": "/getting-started/install-on-ubuntu/#apt-repo", - "text": "Add Vapor's APT repo to get access to all of Vapor's system packages.", - "title": "APT Repo" - }, - { - "location": "/getting-started/install-on-ubuntu/#quick-script", - "text": "Easily add Vapor's APT repo with this handy script. eval $( curl -sL https://apt.vapor.sh ) Note This command requires curl which can be installed using sudo apt-get install curl", - "title": "Quick Script" - }, - { - "location": "/getting-started/install-on-ubuntu/#dockerfile", - "text": "When configuring Ubuntu from a Dockerfile, adding the APT repo can be done via this command: RUN /bin/bash -c $( wget -qO- https://apt.vapor.sh )", - "title": "Dockerfile" - }, - { - "location": "/getting-started/install-on-ubuntu/#manual", - "text": "Or add the repo manually. 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\nsudo apt-get update", - "title": "Manual" - }, - { - "location": "/getting-started/install-on-ubuntu/#install-vapor", - "text": "Now that you have added Vapor's APT repo, you can install the required dependencies. sudo apt-get install swift vapor", - "title": "Install Vapor" - }, - { - "location": "/getting-started/install-on-ubuntu/#verify-installation", - "text": "Double check the installation was successful by running: eval $( curl -sL check.vapor.sh )", - "title": "Verify Installation" - }, - { - "location": "/getting-started/install-on-ubuntu/#next", - "text": "Learn more about the Vapor toolbox CLI in the Toolbox section of the Getting Started section.", - "title": "Next" - }, - { - "location": "/getting-started/install-on-ubuntu/#swiftorg", - "text": "Check out Swift.org 's guide to using downloads if you need more detailed instructions for installing Swift 3.1.", - "title": "Swift.org" - }, - { - "location": "/getting-started/toolbox/", - "text": "Install Toolbox\n\n\nVapor's command line interface provides shortcuts and assistance for common tasks.\n\n\n\n\n\n\nTip\n\n\nIf you do not want to use the Toolbox or templates, checkout the \nManual\n quickstart.\n\n\n\n\nHelp\n\n\nHelp prints useful information about available commands and flags. You can also run the \n--help\n option on any Toolbox command.\n\n\nvapor --help\n\n\n\n\n\nApplication Commands\n\n\nThe \nvapor run\n command is a special toolbox command that forwards to your Vapor application.\n\n\nOnce you've built your application with \nvapor build\n you can use \nvapor run serve\n to boot your application, or \nvapor run help\n to view all available application-level commands. This includes custom commands you may have added to your application.\n\n\n\n\nWarning\n\n\nUsing \nvapor run --help\n will provide information about the \nrun\n command itself and will not forward to your Vapor application.\n\n\n\n\nUpdating\n\n\nThe toolbox should be updated by the package manager it was installed with.\n\n\nHomebrew\n\n\nbrew upgrade vapor\n\n\n\n\n\nAPT\n\n\nsudo apt-get update\nsudo apt-get install vapor\n\n\n\n\n\nTemplates\n\n\nThe toolbox can create a project from the Vapor basic-template or any other git repo.\n\n\nvapor new \nname\n \n[\n--template\n]\n\n\n\n\n\n\n\n\n\n\n\n\nName\n\n\nFlag\n\n\nDescription\n\n\n\n\n\n\n\n\n\n\nAPI\n\n\n--template=api\n\n\nJSON API with Fluent database.\n\n\n\n\n\n\nWeb\n\n\n--template=web\n\n\nHTML website with Leaf templates.\n\n\n\n\n\n\n\n\nView a list of all \ntemplates\n on GitHub.\n\n\n\n\nNote\n\n\nIf you do not specify a template option, the API template will be used.\nThis may change in the future.\n\n\n\n\nOptions\n\n\nThe toolbox will build an absolute URL based on what you pass as the template option. \n\n\n\n\n--template=web\n clones \nhttp://github.com/vapor/web-template\n\n\n--template=user/repo\n clones \nhttp://github.com/user/repo\n.\n\n\n--template=http://example.com/repo-path\n clones the full url given.\n\n\n--branch=foo\n can be used to specify a branch besides \nmaster\n.", - "title": "Toolbox" - }, - { - "location": "/getting-started/toolbox/#install-toolbox", - "text": "Vapor's command line interface provides shortcuts and assistance for common tasks. Tip If you do not want to use the Toolbox or templates, checkout the Manual quickstart.", - "title": "Install Toolbox" - }, - { - "location": "/getting-started/toolbox/#help", - "text": "Help prints useful information about available commands and flags. You can also run the --help option on any Toolbox command. vapor --help", - "title": "Help" - }, - { - "location": "/getting-started/toolbox/#application-commands", - "text": "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.", - "title": "Application Commands" - }, - { - "location": "/getting-started/toolbox/#updating", - "text": "The toolbox should be updated by the package manager it was installed with.", - "title": "Updating" - }, - { - "location": "/getting-started/toolbox/#homebrew", - "text": "brew upgrade vapor", - "title": "Homebrew" - }, - { - "location": "/getting-started/toolbox/#apt", - "text": "sudo apt-get update\nsudo apt-get install vapor", - "title": "APT" - }, - { - "location": "/getting-started/toolbox/#templates", - "text": "The toolbox can create a project from the Vapor basic-template or any other git repo. 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 on GitHub. Note If you do not specify a template option, the API template will be used.\nThis may change in the future.", - "title": "Templates" - }, - { - "location": "/getting-started/toolbox/#options", - "text": "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 .", - "title": "Options" - }, - { - "location": "/getting-started/hello-world/", - "text": "Hello, World\n\n\nThis section assumes you have installed Swift 3.1 and the Vapor Toolbox and have verified they are working.\n\n\n\n\nTip\n\n\nNote: If you don't want to use the Toolbox, follow the \nmanual guide\n.\n\n\n\n\nNew Project\n\n\nLet's start by creating a new project called \"Hello, World\".\n\n\nvapor new Hello --template\n=\napi\n\n\n\n\n\nVapor's folder structure will probably look familiar to you if you have worked with other web frameworks.\n\n\nHello\n\u251c\u2500\u2500 Config\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 app.json\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 crypto.json\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 droplet.json\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 fluent.json\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 server.json\n\u251c\u2500\u2500 Package.pins\n\u251c\u2500\u2500 Package.swift\n\u251c\u2500\u2500 Public\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 Sources\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 App\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 Config+Setup.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 Controllers\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 PostController.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 Droplet+Setup.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 Models\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Post.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Routes\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Routes.swift\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 Run\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.swift\n\u251c\u2500\u2500 Tests\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 AppTests\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 PostControllerTests.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 RouteTests.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Utilities.swift\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 LinuxMain.swift\n\u251c\u2500\u2500 circle.yml\n\u2514\u2500\u2500 license\n\n\n\n\n\nFor our Hello, World project, we will be focusing on the \nRoutes.swift\n file.\n\n\nHello\n\u2514\u2500\u2500 Sources\n \u2514\u2500\u2500 App\n \u2514\u2500\u2500 Routes.swift\n\n\n\n\n\n\n\nTip\n\n\nThe \nvapor new\n command creates a new project with examples and comments about how to use the framework. You can delete these if you want.\n\n\n\n\nCode\n\n\nDroplet\n\n\nLook for the following line in the \nRoutes.swift\n file.\n\n\nfunc\n \nsetupRoutes\n()\n \nthrows\n\n\n\n\n\n\nThis method is where all the routes for our application will be added. \n\n\nRouting\n\n\nIn the scope of the \nbuild\n method, look for the following statement.\n\n\nget\n(\nplaintext\n)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nHello, world!\n\n\n}\n\n\n\n\n\n\nThis creates a new route that will match all \nGET\n requests to \n/plaintext\n.\n\n\nAll route closures are passed an instance of \nRequest\n that contains information such as the URI requested and data sent.\n\n\nThis route simply returns a string, but anything that is \nResponseRepresentable\n can be returned. Learn more in the \nRouting\n section of the guide.\n\n\n\n\nTip\n\n\nXcode 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 \nimport HTTP\n to the top of the file.\n\n\n\n\nCompile \n Run\n\n\nBuilding\n\n\nA 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.\n\n\nvapor\n \nbuild\n\n\n\n\n\n\n\n\nNote\n\n\nvapor build\n runs \nswift build\n in the background.\n\n\n\n\nThe Swift Package Manager will first start by downloading the appropriate dependencies from git. It will then compile and link these dependencies together.\n\n\nWhen the process has completed, you will see \nBuilding Project [Done]\n\n\n\n\nTip\n\n\nIf you see a message like \nunable to execute command: Killed\n, you need to increase your swap space. This can happen if you are running on a machine with limited memory.\n\n\n\n\nRelease\n\n\nBuilding your application in release mode takes longer, but increases performance.\n\n\nvapor build --release\n\n\n\n\n\nServing\n\n\nBoot up the server by running the following command.\n\n\nvapor run serve\n\n\n\n\n\nYou should see a message \nServer starting...\n. \n\n\nYou can now visit \nlocalhost:8080/plaintext\n in your browser or run \n\n\ncurl localhost:8080/plaintext\n\n\n\n\n\n\n\nNote\n\n\nCertain port numbers require super user access to bind. Simply run \nsudo vapor run\n to allow access. If you decide to run on a port besides \n80\n, make sure to direct your browser accordingly.\n\n\n\n\nHello, World\n\n\nYou should see the following output in your browser window.\n\n\nHello, world!\n\n\n\n\n\n\n\nSuccess\n\n\nLike Vapor so far? Click the button below and star the repo to help spread the word! \n\n\n\n\n\n\n\nProduction\n\n\nServing your application in the production environment increases its security and performance.\n\n\nvapor run serve --env\n=\nproduction\n\n\n\n\n\nSome debug messages will be silenced while in the production environment, so make sure to check your logs for errors.\n\n\n\n\nWarning\n\n\nIf you compiled your application with \n--release\n, make sure to add that flag to the \nvapor run\n command as well. e.g., \nvapor run serve --env=production --release\n.\n\n\n\n\nFor more information on deploying your code, check out the \ndeploy section\n.", - "title": "Hello, World" - }, - { - "location": "/getting-started/hello-world/#hello-world", - "text": "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 .", - "title": "Hello, World" - }, - { - "location": "/getting-started/hello-world/#new-project", - "text": "Let's start by creating a new project called \"Hello, World\". vapor new Hello --template = api Vapor's folder structure will probably look familiar to you if you have worked with other web frameworks. Hello\n\u251c\u2500\u2500 Config\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 app.json\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 crypto.json\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 droplet.json\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 fluent.json\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 server.json\n\u251c\u2500\u2500 Package.pins\n\u251c\u2500\u2500 Package.swift\n\u251c\u2500\u2500 Public\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 Sources\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 App\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 Config+Setup.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 Controllers\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 PostController.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 Droplet+Setup.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 Models\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Post.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Routes\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Routes.swift\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 Run\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.swift\n\u251c\u2500\u2500 Tests\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 AppTests\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 PostControllerTests.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 RouteTests.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Utilities.swift\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 LinuxMain.swift\n\u251c\u2500\u2500 circle.yml\n\u2514\u2500\u2500 license For our Hello, World project, we will be focusing on the Routes.swift file. Hello\n\u2514\u2500\u2500 Sources\n \u2514\u2500\u2500 App\n \u2514\u2500\u2500 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.", - "title": "New Project" - }, - { - "location": "/getting-started/hello-world/#code", - "text": "", - "title": "Code" - }, - { - "location": "/getting-started/hello-world/#droplet", - "text": "Look for the following line in the Routes.swift file. func setupRoutes () throws This method is where all the routes for our application will be added.", - "title": "Droplet" - }, - { - "location": "/getting-started/hello-world/#routing", - "text": "In the scope of the build method, look for the following statement. get ( plaintext ) { req in \n 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 that contains information such as the URI requested and data sent. This route simply returns a string, but anything that is ResponseRepresentable can be returned. Learn more in the Routing 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.", - "title": "Routing" - }, - { - "location": "/getting-started/hello-world/#compile-run", - "text": "", - "title": "Compile & Run" - }, - { - "location": "/getting-started/hello-world/#building", - "text": "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. 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.", - "title": "Building" - }, - { - "location": "/getting-started/hello-world/#release", - "text": "Building your application in release mode takes longer, but increases performance. vapor build --release", - "title": "Release" - }, - { - "location": "/getting-started/hello-world/#serving", - "text": "Boot up the server by running the following command. vapor run serve You should see a message Server starting... . You can now visit localhost:8080/plaintext in your browser or run 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.", - "title": "Serving" - }, - { - "location": "/getting-started/hello-world/#hello-world_1", - "text": "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!", - "title": "Hello, World" - }, - { - "location": "/getting-started/hello-world/#production", - "text": "Serving your application in the production environment increases its security and performance. 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 .", - "title": "Production" - }, - { - "location": "/getting-started/manual/", - "text": "Manual Quickstart\n\n\nLearn how to create a Vapor project \nwithout\n the Toolbox using just Swift 3.1 and the Swift Package Manager (SPM).\n\n\nThis document assumes that you have Swift 3.1 installed, if not please refer to \nSwift.org\n before you can continue.\n\n\n\n\nTip\n\n\nIf you'd prefer to use the Toolbox, follow the toolbox guide \nhere\n.\n\n\n\n\nMake new project using SwiftPM\n\n\nOpen your terminal\n\n\n\n\nNote\n\n\nFor our example, we'll be using the Desktop folder.\n\n\n\n\ncd\n ~/Desktop\nmkdir Hello\n\ncd\n Hello\nswift package init --type executable\n\n\n\n\n\nYour folder structure should look like this:\n\n\n\u251c\u2500\u2500 Package.swift\n\u251c\u2500\u2500 Sources\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.swift\n\u2514\u2500\u2500 Tests\n\n\n\n\n\nEdit \nPackage.swift\n\n\nOpen your \nPackage.swift\n file:\n\n\nopen Package.swift\n\n\n\n\n\nAnd add Vapor as a dependency. Here's how your \nPackage.swift\n should look like.\n\n\n// swift-tools-version:3.1\n\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nHello\n,\n\n \ndependencies\n:\n \n[\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/vapor.git\n,\n \nmajorVersion\n:\n \n2\n)\n\n \n]\n\n\n)\n\n\n\n\n\n\n\n\nWarning\n\n\nWe try to keep this document up to date, however, you can view latest releases \nhere\n.\n\n\n\n\nEdit \nmain.swift\n\n\nA simple hello world:\n\n\nimport\n \nVapor\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n()\n\n\n\ndrop\n.\nget\n(\nhello\n)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nHello Vapor\n\n\n}\n\n\n\ntry\n \ndrop\n.\nrun\n()\n\n\n\n\n\n\nCompile \n Run (Development)\n\n\nThe first \nbuild\n command can take a while to fetch dependencies.\n\n\nswift build\n.build/debug/Hello serve\n\n\n\n\n\n\n\nWarning\n\n\nIf different, replace \nHello\n above with the name of your executable (as defined in \nPackage.swift\n).\n\n\n\n\nProduction\n\n\nCompiling in Swift's release mode and setting Vapor's environment to production will make your app more secure and performant.\n\n\nswift build --configuration release\n.build/release/Hello serve --env\n=\nproduction\n\n\n\n\n\nView\n\n\nGo to your favorite browser and visit \nhttp://localhost:8080/hello", - "title": "Manual" - }, - { - "location": "/getting-started/manual/#manual-quickstart", - "text": "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 before you can continue. Tip If you'd prefer to use the Toolbox, follow the toolbox guide here .", - "title": "Manual Quickstart" - }, - { - "location": "/getting-started/manual/#make-new-project-using-swiftpm", - "text": "Open your terminal Note For our example, we'll be using the Desktop folder. cd ~/Desktop\nmkdir Hello cd Hello\nswift package init --type executable Your folder structure should look like this: \u251c\u2500\u2500 Package.swift\n\u251c\u2500\u2500 Sources\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.swift\n\u2514\u2500\u2500 Tests", - "title": "Make new project using SwiftPM" - }, - { - "location": "/getting-started/manual/#edit-packageswift", - "text": "Open your Package.swift file: open Package.swift And add Vapor as a dependency. Here's how your Package.swift should look like. // swift-tools-version:3.1 import PackageDescription let package = Package ( \n name : Hello , \n dependencies : [ \n . Package ( url : https://github.com/vapor/vapor.git , majorVersion : 2 ) \n ] ) Warning We try to keep this document up to date, however, you can view latest releases here .", - "title": "Edit Package.swift" - }, - { - "location": "/getting-started/manual/#edit-mainswift", - "text": "A simple hello world: import Vapor let drop = try Droplet () drop . get ( hello ) { req in \n return Hello Vapor } try drop . run ()", - "title": "Edit main.swift" - }, - { - "location": "/getting-started/manual/#compile-run-development", - "text": "The first build command can take a while to fetch dependencies. swift build\n.build/debug/Hello serve Warning If different, replace Hello above with the name of your executable (as defined in Package.swift ).", - "title": "Compile & Run (Development)" - }, - { - "location": "/getting-started/manual/#production", - "text": "Compiling in Swift's release mode and setting Vapor's environment to production will make your app more secure and performant. swift build --configuration release\n.build/release/Hello serve --env = production", - "title": "Production" - }, - { - "location": "/getting-started/manual/#view", - "text": "Go to your favorite browser and visit http://localhost:8080/hello", - "title": "View" - }, - { - "location": "/getting-started/xcode/", - "text": "Xcode\n\n\nIf you're on a Mac, you can develop your Vapor project using Xcode. \nYou can build, run, and stop your server from within Xcode, as well as use breakpoints to debug your code.\n\n\n\n\nTo use Xcode, you will first need to generate a \n*.xcodeproj\n file.\n\n\nGenerate Project\n\n\nVapor Toolbox\n\n\nTo generate a new Xcode project for a project, use:\n\n\nvapor xcode\n\n\n\n\n\n\n\nTip\n\n\nIf you'd like to automatically open the Xcode project, use \nvapor xcode -y\n\n\n\n\nSelect 'Run'\n\n\nMake sure after generating your Xcode project that you properly select the executable if you're trying to run your application.\n\n\n\n\nManual\n\n\nTo generate a new Xcode project manually.\n\n\nswift package generate-xcodeproj\n\n\n\n\n\nOpen the project and continue normally.", - "title": "Xcode" - }, - { - "location": "/getting-started/xcode/#xcode", - "text": "If you're on a Mac, you can develop your Vapor project using Xcode. \nYou can build, run, and stop your server from within Xcode, as well as use breakpoints to debug your code. To use Xcode, you will first need to generate a *.xcodeproj file.", - "title": "Xcode" - }, - { - "location": "/getting-started/xcode/#generate-project", - "text": "", - "title": "Generate Project" - }, - { - "location": "/getting-started/xcode/#vapor-toolbox", - "text": "To generate a new Xcode project for a project, use: vapor xcode Tip If you'd like to automatically open the Xcode project, use vapor xcode -y", - "title": "Vapor Toolbox" - }, - { - "location": "/getting-started/xcode/#select-run", - "text": "Make sure after generating your Xcode project that you properly select the executable if you're trying to run your application.", - "title": "Select 'Run'" - }, - { - "location": "/getting-started/xcode/#manual", - "text": "To generate a new Xcode project manually. swift package generate-xcodeproj Open the project and continue normally.", - "title": "Manual" - }, - { - "location": "/vapor/folder-structure/", - "text": "Folder Structure\n\n\nThe first step to creating an awesome application is knowing where things are. If you created your project using the \nToolbox\n or from a template, you will already have the folder structure created.\n\n\nIf you are making a Vapor application from scratch, this will show you exactly how to set it up.\n\n\nMinimum Folder Structure\n\n\nWe recommend putting all of your Swift code inside of the \nApp/\n folder. This will allow you to create subfolders in \nApp/\n to organize your models and resources.\n\n\nThis works best with the Swift package manager's restrictions on how packages should be structured.\n\n\n.\n\u251c\u2500\u2500 App\n\u2502 \u2514\u2500\u2500 main.swift\n\u251c\u2500\u2500 Public\n\u2514\u2500\u2500 Package.swift\n\n\n\n\n\nThe \nPublic\n 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.\n\n\n\n\nNote\n\n\nThe \nFileMiddleware\n is responsible for accessing files from the \nPublic\n folder.\n\n\n\n\nModels\n\n\nThe \nModels\n folder is a recommendation of where you can put your database and other models.\n\n\n.\n\u251c\u2500\u2500 App\n. \u2514\u2500\u2500 Models\n. \u2514\u2500\u2500 User.swift\n\n\n\n\n\nControllers\n\n\nThe \nControllers\n folder is a recommendation of where you can put your route controllers.\n\n\n.\n\u251c\u2500\u2500 App\n. \u2514\u2500\u2500 Controllers\n. \u2514\u2500\u2500 UserController.swift\n\n\n\n\n\nViews\n\n\nThe \nViews\n folder in \nResources\n is where Vapor will look when you render views.\n\n\n.\n\u251c\u2500\u2500 App\n\u2514\u2500\u2500 Resources\n \u2514\u2500\u2500 Views\n \u2514\u2500\u2500 user.html\n\n\n\n\n\nThe following code would load the \nuser.html\n file.\n\n\ndrop\n.\nview\n.\nmake\n(\nuser.html\n)\n\n\n\n\n\n\nConfig\n\n\nVapor has a sophisticated configuration system that involves a hierarchy of configuration importance.\n\n\n.\n\u251c\u2500\u2500 App\n\u2514\u2500\u2500 Config\n \u2514\u2500\u2500 app.json // default app.json\n \u2514\u2500\u2500 development\n \u2514\u2500\u2500 app.json // overrides app.json when in development environment\n \u2514\u2500\u2500 production\n \u2514\u2500\u2500 app.json // overrides app.json when in production environment\n \u2514\u2500\u2500 secrets\n \u2514\u2500\u2500 app.json // overrides app.json in all environments, ignored by git\n\n\n\n\n\n.json\n files are structured in the \nConfig\n folder as shown above. The configuration will be applied dependant on where the \n.json\n file exists in the hierarchy. Learn more in \nConfig\n.\n\n\nLearn about changing environments (the \n--env=\n flag) in the \nDroplet\n section.", - "title": "Folder Structure" - }, - { - "location": "/vapor/folder-structure/#folder-structure", - "text": "The first step to creating an awesome application is knowing where things are. If you created your project using the Toolbox 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.", - "title": "Folder Structure" - }, - { - "location": "/vapor/folder-structure/#minimum-folder-structure", - "text": "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. .\n\u251c\u2500\u2500 App\n\u2502 \u2514\u2500\u2500 main.swift\n\u251c\u2500\u2500 Public\n\u2514\u2500\u2500 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.", - "title": "Minimum Folder Structure" - }, - { - "location": "/vapor/folder-structure/#models", - "text": "The Models folder is a recommendation of where you can put your database and other models. .\n\u251c\u2500\u2500 App\n. \u2514\u2500\u2500 Models\n. \u2514\u2500\u2500 User.swift", - "title": "Models" - }, - { - "location": "/vapor/folder-structure/#controllers", - "text": "The Controllers folder is a recommendation of where you can put your route controllers. .\n\u251c\u2500\u2500 App\n. \u2514\u2500\u2500 Controllers\n. \u2514\u2500\u2500 UserController.swift", - "title": "Controllers" - }, - { - "location": "/vapor/folder-structure/#views", - "text": "The Views folder in Resources is where Vapor will look when you render views. .\n\u251c\u2500\u2500 App\n\u2514\u2500\u2500 Resources\n \u2514\u2500\u2500 Views\n \u2514\u2500\u2500 user.html The following code would load the user.html file. drop . view . make ( user.html )", - "title": "Views" - }, - { - "location": "/vapor/folder-structure/#config", - "text": "Vapor has a sophisticated configuration system that involves a hierarchy of configuration importance. .\n\u251c\u2500\u2500 App\n\u2514\u2500\u2500 Config\n \u2514\u2500\u2500 app.json // default app.json\n \u2514\u2500\u2500 development\n \u2514\u2500\u2500 app.json // overrides app.json when in development environment\n \u2514\u2500\u2500 production\n \u2514\u2500\u2500 app.json // overrides app.json when in production environment\n \u2514\u2500\u2500 secrets\n \u2514\u2500\u2500 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 . Learn about changing environments (the --env= flag) in the Droplet section.", - "title": "Config" - }, - { - "location": "/vapor/droplet/", - "text": "Droplet\n\n\nThe \nDroplet\n 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.\n\n\n\n\nTip\n\n\nNormally applications will only have one Droplet. However, for advanced use cases, it is possible to create more than one.\n\n\n\n\nInitialization\n\n\nAs you have probably already seen, the only thing required to create an instance of \nDroplet\n is to import Vapor.\n\n\nimport\n \nVapor\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n()\n\n\n\n// your magic here\n\n\n\ntry\n \ndrop\n.\nrun\n()\n\n\n\n\n\n\nCreation of the \nDroplet\n normally happens in the \nmain.swift\n file. \n\n\n\n\nNote\n\n\nFor the sake of simplicity, most of the documentations sample code uses just the \nmain.swift\n file. You can read more about packages and modules in the Swift Package Manager \nconceptual overview\n.\n\n\n\n\nEnvironment\n\n\nThe \nenvironment\n is accessible via the config of the droplet.\nIt contains the current environment your application is running in. Usually development, testing, or production.\n\n\nif\n \ndrop\n.\nconfig\n.\nenvironment\n \n==\n \n.\nproduction\n \n{\n\n \n...\n\n\n}\n\n\n\n\n\n\nThe environment affects \nConfig\n and \nLogging\n. The environment is \ndevelopment\n by default. To change it, pass the \n--env=\n flag as an argument.\n\n\nvapor run serve --env\n=\nproduction\n\n\n\n\n\nIf you are in Xcode, you can pass arguments through the scheme editor.\n\n\n\n\nWarning\n\n\nDebug logs can reduce the number of requests your application can handle per second. Enable production mode to silence non-critical logs.\n\n\n\n\nConfig Directory\n\n\nThe \nworkDir\n property contains a path to the current working directory of the application. Vapor uses this property to find the folders related to your project, such as \nResources\n, \nPublic\n, and \nConfig\n.\n\n\nprint\n(\ndrop\n.\nconfig\n.\nworkDir\n)\n \n// /var/www/my-project/\n\n\n\n\n\n\nVapor automatically determines the working directory in most situations. However, you may need to manually set it for advanced use cases.\n\n\nYou can override the working directory through the \nDroplet\n's initializer, or by passing the \n--workdir\n argument.\n\n\nvapor run serve --workdir\n=\n/var/www/my-project\n\n\n\n\n\n\nModifying Properties\n\n\nThe \nDroplet\n's properties can be changed programmatically or through configuration.\n\n\nProgrammatic\n\n\nProperties on the \nDroplet\n are constant and can be overridden through the init method.\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nserver\n:\n \nMyServerType\n.\nself\n)\n\n\n\n\n\n\nHere the type of server the \nDroplet\n uses is changed to a custom type. When the \nDroplet\n is run, this custom server type will be booted instead of the default server.\n\n\n\n\nWarning\n\n\nUsing the init method manually can override configured properties.\n\n\n\n\nConfigurable\n\n\nIf you want to modify a property of the \nDroplet\n only in certain cases, you can use \naddConfigurable\n. Say for example you want to email error logs to yourself in production, but you don't want to spam your inbox while developing.\n\n\nlet\n \nconfig\n \n=\n \ntry\n \nConfig\n()\n\n\nconfig\n.\naddConfigurable\n(\nlog\n:\n \nMyEmailLogger\n.\ninit\n,\n \nname\n:\n \nemail\n)\n\n\n\nlet\n \ndrop\n \n=\n \nDroplet\n(\nconfig\n)\n\n\n\n\n\n\nThe \nDroplet\n will continue to use the default logger until you modify the \nConfig/droplet.json\n file to point to your email logger. If this is done in \nConfig/production/droplet.json\n, then your logger will only be used in production.\n\n\n{\n\n \nlog\n:\n \nemail\n\n\n}\n\n\n\n\n\n\nSupported Properties\n\n\n\n\n\n\n\n\nProperty\n\n\nType\n\n\ndroplet.json\n key\n\n\nConfig Initializable\n\n\n\n\n\n\n\n\n\n\nserver\n\n\nServerProtocol.Type\n\n\nserver\n\n\nno\n\n\n\n\n\n\nclient\n\n\nClientProtocol.Type\n\n\nclient\n\n\nno\n\n\n\n\n\n\nlog\n\n\nLogProtocol\n\n\nlog\n\n\nyes\n\n\n\n\n\n\nhash\n\n\nHashProtocol\n\n\nhash\n\n\nyes\n\n\n\n\n\n\ncipher\n\n\nCipherProtocol\n\n\ncipher\n\n\nyes\n\n\n\n\n\n\nmiddleware\n\n\nMiddleware\n\n\nmiddleware.[server,client]\n\n\nno\n\n\n\n\n\n\nconsole\n\n\nConsoleProtocol\n\n\nconsole\n\n\nyes\n\n\n\n\n\n\ncache\n\n\nCacheProtocol\n\n\ncache\n\n\nyes\n\n\n\n\n\n\n\n\nExample\n\n\nLet's create a custom logger to demonstrate Vapor's configurable properties.\n\n\nAllCapsLogger.swift\n\n\nfinal\n \nclass\n \nAllCapsLogger\n:\n \nLogProtocol\n \n{\n\n \nvar\n \nenabled\n:\n \n[\nLogLevel\n]\n \n=\n \n[]\n\n \nfunc\n \nlog\n(\n_\n \nlevel\n:\n \nLogLevel\n,\n \nmessage\n:\n \nString\n,\n \nfile\n:\n \nString\n,\n \nfunction\n:\n \nString\n,\n \nline\n:\n \nInt\n)\n \n{\n\n \nprint\n(\nmessage\n.\nuppercased\n()\n \n+\n \n!!!\n)\n\n \n}\n\n\n}\n\n\n\nextension\n \nAllCapsLogger\n:\n \nConfigInitializable\n \n{\n\n \nconvenience\n \ninit\n(\nconfig\n:\n \nConfig\n)\n \nthrows\n \n{\n\n \nself\n.\ninit\n()\n\n \n}\n\n\n}\n\n\n\n\n\n\nNow add the logger to the Droplet using the \naddConfigurable\n method for logs.\n\n\nmain.swift\n\n\nlet\n \nconfig\n \n=\n \ntry\n \nConfig\n()\n\n\nconfig\n.\naddConfigurable\n(\nlog\n:\n \nAllCapsLogger\n.\ninit\n,\n \nname\n:\n \nall-caps\n)\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nconfig\n)\n\n\n\n\n\n\nWhenever the \n\"log\"\n property is set to \n\"all-caps\"\n in the \ndroplet.json\n, our new logger will be used. \n\n\nConfig/development/droplet.json\n\n\n{\n\n \nlog\n:\n \nall-caps\n\n\n}\n\n\n\n\n\n\nHere we are setting our logger only in the \ndevelopment\n environment. All other environments will use Vapor's default logger.\n\n\nConfig Initializable\n\n\nFor an added layer of convenience, you can allow your custom types to be initialized from configuration files.\n\n\nIn our previous example, we initialized an \nAllCapsLogger\n before adding it to the Droplet.\n\n\nLet's say we want to allow our project to configure how many exclamation points get added with each log message.\n\n\nAllCapsLogger.swift\n\n\nfinal\n \nclass\n \nAllCapsLogger\n:\n \nLogProtocol\n \n{\n\n \nvar\n \nenabled\n:\n \n[\nLogLevel\n]\n \n=\n \n[]\n\n \nlet\n \nexclamationCount\n:\n \nInt\n\n\n \ninit\n(\nexclamationCount\n:\n \nInt\n)\n \n{\n\n \nself\n.\nexclamationCount\n \n=\n \nexclamationCount\n\n \n}\n\n\n \nfunc\n \nlog\n(\n_\n \nlevel\n:\n \nLogLevel\n,\n \nmessage\n:\n \nString\n,\n \nfile\n:\n \nString\n,\n \nfunction\n:\n \nString\n,\n \nline\n:\n \nInt\n)\n \n{\n\n \nprint\n(\nmessage\n.\nuppercased\n()\n \n+\n \nString\n(\nrepeating\n:\n \n!\n,\n \ncount\n:\n \nexclamationCount\n))\n\n \n}\n\n\n}\n\n\n\nextension\n \nAllCapsLogger\n:\n \nConfigInitializable\n \n{\n\n \nconvenience\n \ninit\n(\nconfig\n:\n \nConfig\n)\n \nthrows\n \n{\n\n \nlet\n \ncount\n \n=\n \nconfig\n[\nallCaps\n,\n \nexclamationCount\n]?.\nint\n \n??\n \n3\n\n \nself\n.\ninit\n(\nexclamationCount\n:\n \ncount\n)\n\n \n}\n \n\n}\n\n\n\n\n\n\n\n\nNote\n\n\nThe first parameter to \nconfig\n is the name of the file.\n\n\n\n\nNow that we have conformed our logger to \nConfigInitializable\n, we can pass just the type name to \naddConfigurable\n.\n\n\nmain.swift\n\n\nlet\n \nconfig\n \n=\n \ntry\n \nConfig\n()\n\n\nconfig\n.\naddConfigurable\n(\nlog\n:\n \nAllCapsLogger\n.\ninit\n,\n \nname\n:\n \nall-caps\n)\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nconfig\n)\n\n\n\n\n\n\nNow if you add a file named \nallCaps.json\n to the \nConfig\n folder, you can configure the logger.\n\n\nallCaps.json\n\n\n{\n\n \nexclamationCount\n:\n \n5\n\n\n}\n\n\n\n\n\n\nWith this configurable abstraction, you can easily change how your application functions in different environments without needing to hard code these values into your source code.", - "title": "Droplet" - }, - { - "location": "/vapor/droplet/#droplet", - "text": "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. Tip Normally applications will only have one Droplet. However, for advanced use cases, it is possible to create more than one.", - "title": "Droplet" - }, - { - "location": "/vapor/droplet/#initialization", - "text": "As you have probably already seen, the only thing required to create an instance of Droplet is to import Vapor. import Vapor let drop = try Droplet () // your magic here try drop . run () Creation of the Droplet normally happens in the main.swift file. Note For the sake of simplicity, most of the documentations sample code uses just the main.swift file. You can read more about packages and modules in the Swift Package Manager conceptual overview .", - "title": "Initialization" - }, - { - "location": "/vapor/droplet/#environment", - "text": "The environment is accessible via the config of the droplet.\nIt contains the current environment your application is running in. Usually development, testing, or production. if drop . config . environment == . production { \n ... } The environment affects Config and Logging . The environment is development by default. To change it, pass the --env= flag as an argument. vapor run serve --env = production If you are in Xcode, you can pass arguments through the scheme editor. Warning Debug logs can reduce the number of requests your application can handle per second. Enable production mode to silence non-critical logs.", - "title": "Environment" - }, - { - "location": "/vapor/droplet/#config-directory", - "text": "The workDir property contains a path to the current working directory of the application. Vapor uses this property to find the folders related to your project, such as Resources , Public , and Config . print ( drop . config . workDir ) // /var/www/my-project/ Vapor automatically determines the working directory in most situations. However, you may need to manually set it for advanced use cases. You can override the working directory through the Droplet 's initializer, or by passing the --workdir argument. vapor run serve --workdir = /var/www/my-project", - "title": "Config Directory" - }, - { - "location": "/vapor/droplet/#modifying-properties", - "text": "The Droplet 's properties can be changed programmatically or through configuration.", - "title": "Modifying Properties" - }, - { - "location": "/vapor/droplet/#programmatic", - "text": "Properties on the Droplet are constant and can be overridden through the init method. let drop = try Droplet ( 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. Warning Using the init method manually can override configured properties.", - "title": "Programmatic" - }, - { - "location": "/vapor/droplet/#configurable", - "text": "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. let config = try Config () config . addConfigurable ( log : MyEmailLogger . init , name : email ) let drop = Droplet ( config ) 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. { \n log : email }", - "title": "Configurable" - }, - { - "location": "/vapor/droplet/#supported-properties", - "text": "Property Type droplet.json key Config Initializable server ServerProtocol.Type server no client ClientProtocol.Type client no log LogProtocol log yes hash HashProtocol hash yes cipher CipherProtocol cipher yes middleware Middleware middleware.[server,client] no console ConsoleProtocol console yes cache CacheProtocol cache yes", - "title": "Supported Properties" - }, - { - "location": "/vapor/droplet/#example", - "text": "Let's create a custom logger to demonstrate Vapor's configurable properties. AllCapsLogger.swift final class AllCapsLogger : LogProtocol { \n var enabled : [ LogLevel ] = [] \n func log ( _ level : LogLevel , message : String , file : String , function : String , line : Int ) { \n print ( message . uppercased () + !!! ) \n } } extension AllCapsLogger : ConfigInitializable { \n convenience init ( config : Config ) throws { \n self . init () \n } } Now add the logger to the Droplet using the addConfigurable method for logs. main.swift let config = try Config () config . addConfigurable ( log : AllCapsLogger . init , name : all-caps ) let drop = try Droplet ( config ) Whenever the \"log\" property is set to \"all-caps\" in the droplet.json , our new logger will be used. Config/development/droplet.json { \n log : all-caps } Here we are setting our logger only in the development environment. All other environments will use Vapor's default logger.", - "title": "Example" - }, - { - "location": "/vapor/droplet/#config-initializable", - "text": "For an added layer of convenience, you can allow your custom types to be initialized from configuration files. In our previous example, we initialized an AllCapsLogger before adding it to the Droplet. Let's say we want to allow our project to configure how many exclamation points get added with each log message. AllCapsLogger.swift final class AllCapsLogger : LogProtocol { \n var enabled : [ LogLevel ] = [] \n let exclamationCount : Int \n\n init ( exclamationCount : Int ) { \n self . exclamationCount = exclamationCount \n } \n\n func log ( _ level : LogLevel , message : String , file : String , function : String , line : Int ) { \n print ( message . uppercased () + String ( repeating : ! , count : exclamationCount )) \n } } extension AllCapsLogger : ConfigInitializable { \n convenience init ( config : Config ) throws { \n let count = config [ allCaps , exclamationCount ]?. int ?? 3 \n self . init ( exclamationCount : count ) \n } } Note The first parameter to config is the name of the file. Now that we have conformed our logger to ConfigInitializable , we can pass just the type name to addConfigurable . main.swift let config = try Config () config . addConfigurable ( log : AllCapsLogger . init , name : all-caps ) let drop = try Droplet ( config ) Now if you add a file named allCaps.json to the Config folder, you can configure the logger. allCaps.json { \n exclamationCount : 5 } With this configurable abstraction, you can easily change how your application functions in different environments without needing to hard code these values into your source code.", - "title": "Config Initializable" - }, - { - "location": "/vapor/views/", - "text": "Views\n\n\nViews return HTML data from your application. They can be created from pure HTML documents or passed through renderers such as \nLeaf\n.\n\n\nViews Directory\n\n\nViews are stored in \nResources/Views\n. They are created by calling the \nview\n method on \nDroplet\n.\n\n\nHTML\n\n\nReturning HTML, or any other non-rendered document, is simple. Just use the path of the document relative to the views directory.\n\n\ndrop\n.\nget\n(\nhtml\n)\n \n{\n \nrequest\n \nin\n\n \nreturn\n \ntry\n \ndrop\n.\nview\n.\nmake\n(\nindex.html\n)\n\n\n}\n\n\n\n\n\n\nTemplating\n\n\nTemplated documents like \nLeaf\n can take a \nContext\n.\n\n\ndrop\n.\nget\n(\ntemplate\n)\n \n{\n \nrequest\n \nin\n\n \nreturn\n \ntry\n \ndrop\n.\nview\n.\nmake\n(\nwelcome\n,\n \n[\n\n \nmessage\n:\n \nHello, world!\n\n \n])\n\n\n}\n\n\n\n\n\n\nThis context will be rendered in the view dynamically based on the \nViewRenderer\n used.\n\n\nPublic Resources\n\n\nAny resources that your views need, such as images, styles, and scripts, should be placed in the \nPublic\n folder at the root of your application.\n\n\nView Renderer\n\n\nAny class that conforms to \nViewRenderer\n can be added to our droplet. \n\n\nimport\n \nVapor\n\n\nimport\n \nVaporLeaf\n\n\n\nlet\n \ndrop\n \n=\n \nDroplet\n()\n\n\n\ndrop\n.\nview\n \n=\n \nLeafRenderer\n(\nviewsDir\n:\n \ndrop\n.\nviewsDir\n)\n\n\n\n\n\n\nAvailable Renderers\n\n\nSearch GitHub\n for Vapor view \nproviders\n that can be added to your application.", - "title": "Views" - }, - { - "location": "/vapor/views/#views", - "text": "Views return HTML data from your application. They can be created from pure HTML documents or passed through renderers such as Leaf .", - "title": "Views" - }, - { - "location": "/vapor/views/#views-directory", - "text": "Views are stored in Resources/Views . They are created by calling the view method on Droplet .", - "title": "Views Directory" - }, - { - "location": "/vapor/views/#html", - "text": "Returning HTML, or any other non-rendered document, is simple. Just use the path of the document relative to the views directory. drop . get ( html ) { request in \n return try drop . view . make ( index.html ) }", - "title": "HTML" - }, - { - "location": "/vapor/views/#templating", - "text": "Templated documents like Leaf can take a Context . drop . get ( template ) { request in \n return try drop . view . make ( welcome , [ \n message : Hello, world! \n ]) } This context will be rendered in the view dynamically based on the ViewRenderer used.", - "title": "Templating" - }, - { - "location": "/vapor/views/#public-resources", - "text": "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.", - "title": "Public Resources" - }, - { - "location": "/vapor/views/#view-renderer", - "text": "Any class that conforms to ViewRenderer can be added to our droplet. import Vapor import VaporLeaf let drop = Droplet () drop . view = LeafRenderer ( viewsDir : drop . viewsDir )", - "title": "View Renderer" - }, - { - "location": "/vapor/views/#available-renderers", - "text": "Search GitHub for Vapor view providers that can be added to your application.", - "title": "Available Renderers" - }, - { - "location": "/vapor/controllers/", - "text": "Controllers\n\n\nControllers help you organize related functionality into a single place. They can also be used to create RESTful resources.\n\n\nBasic\n\n\nA basic controller looks like the following:\n\n\nimport\n \nVapor\n\n\nimport\n \nHTTP\n\n\n\nfinal\n \nclass\n \nHelloController\n \n{\n\n \nfunc\n \nsayHello\n(\n_\n \nreq\n:\n \nRequest\n)\n \nthrows\n \n-\n \nResponseRepresentable\n \n{\n\n \nguard\n \nlet\n \nname\n \n=\n \nreq\n.\ndata\n[\nname\n]?.\nstring\n \nelse\n \n{\n \n \nthrow\n \nAbort\n(.\nbadRequest\n)\n\n \n}\n\n\n \nreturn\n \nHello, \n\\(\nname\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nSimple controllers don't need to conform to any protocols. You are free to design them however you see fit.\n\n\nRegistering\n\n\nThe 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 \n(Request) throws -\n ResponseRepresentable\n. \nRequest\n and \nResponseRepresentable\n are made available by importing the \nHTTP\n module.\n\n\nimport\n \nVapor\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n()\n\n\n\nlet\n \nhc\n \n=\n \nHelloController\n()\n\n\ndrop\n.\nget\n(\nhello\n,\n \nhandler\n:\n \nhc\n.\nsayHello\n)\n\n\n\n\n\n\nSince the signature of our \nsayHello\n method matches the signature of the closure for the \ndrop.get\n method, we can pass it directly. If you want to test it locally simply open \nhttp://localhost:8080/hello?name=John\n.\n\n\nType Safe\n\n\nYou can also use controller methods with type-safe routing.\n\n\nfinal\n \nclass\n \nHelloController\n \n{\n\n \n...\n\n\n \nfunc\n \nsayHelloAlternate\n(\n_\n \nreq\n:\n \nRequest\n)\n \nthrows\n \n-\n \nResponseRepresentable\n \n{\n\n \nlet\n \nname\n:\n \nString\n \n=\n \ntry\n \nreq\n.\nparameters\n.\nnext\n(\nString\n.\nself\n)\n\n \nreturn\n \nHello, \n\\(\nname\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nWe add a new method called \nsayHelloAlternate\n to the \nHelloController\n that fetches a \nString\n from the request's parameters.\n\n\nlet\n \nhc\n \n=\n \nHelloController\n()\n\n\ndrop\n.\nget\n(\nhello\n,\n \nString\n.\nparameter\n,\n \nhandler\n:\n \nhc\n.\nsayHelloAlternate\n)\n\n\n\n\n\n\nSince \ndrop.get\n accepts a signature \n(Request) throws -\n ResponseRepresentable\n, our method can now be used as the closure for this route. In this case to test it locally open \nhttp://localhost:8080/hello/John\n.\n\n\n\n\nNote\n\n\nRead more about type safe routing in the \nRouting Parameters\n section.\n\n\n\n\nResources\n\n\nControllers that conform to \nResourceRepresentable\n can be easily registered into a router as a RESTful resource. Let's look at an example of a \nUserController\n.\n\n\nfinal\n \nclass\n \nUserController\n \n{\n\n \nfunc\n \nindex\n(\n_\n \nreq\n:\n \nRequest\n)\n \nthrows\n \n-\n \nResponseRepresentable\n \n{\n\n \nreturn\n \ntry\n \nUser\n.\nall\n().\nmakeJSON\n()\n\n \n}\n\n\n \nfunc\n \nshow\n(\n_\n \nreq\n:\n \nRequest\n)\n \nthrows\n \n-\n \nResponseRepresentable\n \n{\n\n \nlet\n \nuser\n \n=\n \ntry\n \nreq\n.\nparameters\n.\nnext\n(\nUser\n.\nself\n)\n\n \nreturn\n \nuser\n\n \n}\n\n\n}\n\n\n\n\n\n\nHere is a typical user controller with an \nindex\n and \nshow\n route. Indexing returns a JSON list of all users and showing returns a JSON representation of a single user.\n\n\nWe \ncould\n register the controller like so:\n\n\nlet\n \nusers\n \n=\n \nUserController\n()\n\n\ndrop\n.\nget\n(\nusers\n,\n \nhandler\n:\n \nusers\n.\nindex\n)\n\n\ndrop\n.\nget\n(\nusers\n,\n \nUser\n.\nself\n,\n \nhandler\n:\n \nusers\n.\nshow\n)\n\n\n\n\n\n\nBut \nResourceRepresentable\n makes this standard RESTful structure easy.\n\n\nextension\n \nUserController\n:\n \nResourceRepresentable\n \n{\n\n \nfunc\n \nmakeResource\n()\n \n-\n \nResource\nUser\n \n{\n\n \nreturn\n \nResource\n(\n\n \nindex\n:\n \nindex\n,\n\n \nshow\n:\n \nshow\n\n \n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nConforming \nUserController\n to \nResourceRepresentable\n requires that the signatures of \nthe \nindex\n and \nshow\n methods match what the \nResource\nUser\n is expecting.\n\n\nNow that \nUserController\n conforms to \nResourceRepresentable\n, registering the routes is easy.\n\n\nlet\n \nusers\n \n=\n \nUserController\n()\n\n\ndrop\n.\nresource\n(\nusers\n,\n \nusers\n)\n\n\n\n\n\n\ndrop.resource\n will take care of registering only the routes that have been supplied by the call to \nmakeResource()\n. In this case, only the \nindex\n and \nshow\n routes will be supplied.\n\n\nActions\n\n\nBelow is a table describing all of the actions available.\n\n\n\n\n\n\n\n\nAction\n\n\nMethod\n\n\nPath\n\n\nNote\n\n\n\n\n\n\n\n\n\n\nindex\n\n\nGET\n\n\n/users\n\n\nReturns all users, optionally filtered by the request data.\n\n\n\n\n\n\nstore\n\n\nPOST\n\n\n/users\n\n\nCreates a new user from the request data.\n\n\n\n\n\n\nshow\n\n\nGET\n\n\n/users/:id\n\n\nReturns the user with the ID supplied in the path.\n\n\n\n\n\n\nreplace\n\n\nPUT\n\n\n/users/:id\n\n\nUpdates the specified user, setting any fields not present in the request data to \nnil\n.\n\n\n\n\n\n\nupdate\n\n\nPATCH\n\n\n/users/:id\n\n\nUpdates the specified user, only modifying fields present in the request data.\n\n\n\n\n\n\ndestroy\n\n\nDELETE\n\n\n/users/:id\n\n\nDeletes the specified user.\n\n\n\n\n\n\nclear\n\n\nDELETE\n\n\n/users\n\n\nDeletes all users, optionally filtered by the request data.\n\n\n\n\n\n\ncreate\n\n\nGET\n\n\n/users/create\n\n\nDisplays a form for creating a new user.\n\n\n\n\n\n\nedit\n\n\nGET\n\n\n/users/:id/edit\n\n\nDisplays a form for editing the specified user.\n\n\n\n\n\n\naboutItem\n\n\nOPTIONS\n\n\n/users/:id\n\n\nMeta action. Displays information about which actions are supported.\n\n\n\n\n\n\naboutMultiple\n\n\nOPTIONS\n\n\n/users\n\n\nMeta action. Displays information about which actions are supported.\n\n\n\n\n\n\n\n\n\n\nNote\n\n\nThe \naboutItem\n and \naboutMultiple\n meta actions are implemented automatically if not overridden. \n\n\n\n\n\n\nTip\n\n\nThe difference between \nreplace\n and \nupdate\n is subtle but important:\nIf a field does not exist in the request data (for example, the user's age is missing),\n\nupdate\n should simply not update that field where as \nreplace\n should set it to \nnil\n.\nIf required data is missing from a \nreplace\n request, an error should be thrown.\n\n\n\n\nFolder\n\n\nControllers can go anywhere in your application, but they are most often stored in the \nApp/Controllers/\n directory. \n\n\n\n\nTip\n\n\nIf 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.", - "title": "Controllers" - }, - { - "location": "/vapor/controllers/#controllers", - "text": "Controllers help you organize related functionality into a single place. They can also be used to create RESTful resources.", - "title": "Controllers" - }, - { - "location": "/vapor/controllers/#basic", - "text": "A basic controller looks like the following: import Vapor import HTTP final class HelloController { \n func sayHello ( _ req : Request ) throws - ResponseRepresentable { \n guard let name = req . data [ name ]?. string else { \n throw Abort (. badRequest ) \n } \n\n return Hello, \\( name ) \n } } Simple controllers don't need to conform to any protocols. You are free to design them however you see fit.", - "title": "Basic" - }, - { - "location": "/vapor/controllers/#registering", - "text": "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. import Vapor let drop = try Droplet () 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. If you want to test it locally simply open http://localhost:8080/hello?name=John .", - "title": "Registering" - }, - { - "location": "/vapor/controllers/#type-safe", - "text": "You can also use controller methods with type-safe routing. final class HelloController { \n ... \n\n func sayHelloAlternate ( _ req : Request ) throws - ResponseRepresentable { \n let name : String = try req . parameters . next ( String . self ) \n return Hello, \\( name ) \n } } We add a new method called sayHelloAlternate to the HelloController that fetches a String from the request's parameters. let hc = HelloController () drop . get ( hello , String . parameter , handler : hc . sayHelloAlternate ) Since drop.get accepts a signature (Request) throws - ResponseRepresentable , our method can now be used as the closure for this route. In this case to test it locally open http://localhost:8080/hello/John . Note Read more about type safe routing in the Routing Parameters section.", - "title": "Type Safe" - }, - { - "location": "/vapor/controllers/#resources", - "text": "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 . final class UserController { \n func index ( _ req : Request ) throws - ResponseRepresentable { \n return try User . all (). makeJSON () \n } \n\n func show ( _ req : Request ) throws - ResponseRepresentable { \n let user = try req . parameters . next ( User . self ) \n return user \n } } 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: 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. extension UserController : ResourceRepresentable { \n func makeResource () - Resource User { \n return Resource ( \n index : index , \n show : show \n ) \n } } Conforming UserController to ResourceRepresentable requires that the signatures of \nthe index and show methods match what the Resource User is expecting. Now that UserController conforms to ResourceRepresentable , registering the routes is easy. 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.", - "title": "Resources" - }, - { - "location": "/vapor/controllers/#actions", - "text": "Below is a table describing all of the actions available. Action Method Path Note index GET /users Returns all users, optionally filtered by the request data. store POST /users Creates a new user from the request data. show GET /users/:id Returns the user with the ID supplied in the path. replace PUT /users/:id Updates the specified user, setting any fields not present in the request data to nil . update PATCH /users/:id Updates the specified user, only modifying fields present in the request data. destroy DELETE /users/:id Deletes the specified user. clear DELETE /users Deletes all users, optionally filtered by the request data. create GET /users/create Displays a form for creating a new user. edit GET /users/:id/edit Displays a form for editing the specified user. aboutItem OPTIONS /users/:id Meta action. Displays information about which actions are supported. aboutMultiple OPTIONS /users Meta action. Displays information about which actions are supported. Note The aboutItem and aboutMultiple meta actions are implemented automatically if not overridden. Tip The difference between replace and update is subtle but important:\nIf a field does not exist in the request data (for example, the user's age is missing), update should simply not update that field where as replace should set it to nil .\nIf required data is missing from a replace request, an error should be thrown.", - "title": "Actions" - }, - { - "location": "/vapor/controllers/#folder", - "text": "Controllers can go anywhere in your application, but they are most often stored in the App/Controllers/ directory. Tip 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.", - "title": "Folder" - }, - { - "location": "/vapor/provider/", - "text": "Provider\n\n\nThe \nProvider\n protocol creates a simple and predictable way for adding functionality and third party packages to your Vapor project.\n\n\nAdding a Provider\n\n\nAdding a provider to your application takes 2-3 steps.\n\n\nAdd Package\n\n\nAll of Vapor's providers end with the \n-provider\n syntax. You can see a list of \navailable providers\n by searching on our GitHub.\n\n\nTo add the provider to your package, add it as a dependency in your \nPackage.swift\n file.\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nMyApp\n,\n\n \ndependencies\n:\n \n[\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/vapor.git\n,\n \nmajorVersion\n:\n \n2\n),\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/mysql-provider.git\n,\n \nmajorVersion\n:\n \n2\n)\n\n \n]\n\n\n)\n\n\n\n\n\n\n\n\nWarning\n\n\nAlways run \nvapor update\n or \nvapor clean\n after editing your \nPackage.swift\n file.\n\n\n\n\nImport\n\n\nOnce the provider has been added, you can import it using \nimport VaporFoo\n where \nFoo\n is the name of the provider.\n\n\nHere is what importing the MySQL provider looks like:\n\n\nimport\n \nVapor\n\n\nimport\n \nMySQLProvider\n\n\n\n\n\n\nAdd to Droplet\n\n\nEvery provider comes with a class named \nProvider\n. Add this class to your Droplet using the \naddProvider\n method. \n\n\nlet\n \nconfig\n \n=\n \ntry\n \nConfig\n()\n\n\ntry\n \nconfig\n.\naddProvider\n(\nMySQLProvider\n.\nProvider\n.\nself\n)\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nconfig\n)\n\n\n\n// ...\n\n\n\ndrop\n.\nrun\n()\n\n\n\n\n\n\nConfiguration\n\n\nSome drivers may require a configuration file. For example, \nMySQLProvider\n requires a \nConfig/mysql.json\n file like the following:\n\n\n{\n\n \nhostname\n:\n \nlocalhost\n,\n\n \nuser\n:\n \nroot\n,\n\n \npassword\n:\n \n,\n\n \ndatabase\n:\n \nvapor\n\n\n}\n\n\n\n\n\n\nYou will receive an error during the \nDroplet\n's initialization if a configuration file is required.\n\n\n\n\nTip\n\n\nStoring sensitive configuration files (ones that contain passwords) in the \nConfig/secrets\n folder will prevent them from being tracked by git.\n\n\n\n\nManual\n\n\nSome providers can be configured manually by using the Provider's init method. This method can be used instead of configuration files.\n\n\nlet\n \nmysqlProvider\n \n=\n \nVaporMySQL\n.\nProvider\n(\nhost\n:\n \nlocalhost\n,\n \nuser\n:\n \nroot\n,\n \npassword\n:\n \n,\n \ndatabase\n:\n \nvapor\n)\n\n\ntry\n \nconfig\n.\naddProvider\n(\nmysqlProvider\n)\n\n\n\n\n\n\nCreate a Provider\n\n\nCreating a provider is easy, you just need to create a package with a class \nProvider\n that conforms to \nVapor.Provider\n.\n\n\nExample\n\n\nHere is what a provider for an example \nFoo\n package would look like. All the provider does is take a message, then print the message when the \nDroplet\n starts.\n\n\nimport\n \nVapor\n\n\n\npublic\n \nfinal\n \nclass\n \nProvider\n:\n \nVapor\n.\nProvider\n \n{\n\n \npublic\n \nlet\n \nmessage\n:\n \nString\n\n\n \npublic\n \nconvenience\n \ninit\n(\nconfig\n:\n \nConfig\n)\n \nthrows\n \n{\n\n \nguard\n \nlet\n \nmessage\n \n=\n \nconfig\n[\nfoo\n,\n \nmessage\n].\nstring\n \nelse\n \n{\n\n \nthrow\n \nConfigError\n.\nmissing\n(\nkey\n:\n \n[\nmessage\n],\n \nfile\n:\n \nfoo\n,\n \ndesiredType\n:\n \nString\n.\nself\n)\n\n \n}\n\n\n \nself\n.\ninit\n(\nmessage\n:\n \nmessage\n)\n\n \n}\n\n\n \npublic\n \ninit\n(\nmessage\n:\n \nString\n)\n \n{\n\n \nself\n.\nmessage\n \n=\n \nmessage\n\n \n}\n\n\n \npublic\n \nfunc\n \nboot\n(\n_\n \ndrop\n:\n \nDroplet\n)\n \n{\n \n}\n\n\n \npublic\n \nfunc\n \nbeforeRun\n(\n_\n \ndrop\n:\n \nDroplet\n)\n \n{\n\n \ndrop\n.\nconsole\n.\ninfo\n(\nmessage\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nThis provider wil require a \nConfig/foo.json\n file that looks like:\n\n\n{\n\n \nmessage\n:\n \nThe message to output\n\n\n}\n\n\n\n\n\n\nThe provider can also be initialized manually with the \ninit(message: String)\n init.", - "title": "Provider" - }, - { - "location": "/vapor/provider/#provider", - "text": "The Provider protocol creates a simple and predictable way for adding functionality and third party packages to your Vapor project.", - "title": "Provider" - }, - { - "location": "/vapor/provider/#adding-a-provider", - "text": "Adding a provider to your application takes 2-3 steps.", - "title": "Adding a Provider" - }, - { - "location": "/vapor/provider/#add-package", - "text": "All of Vapor's providers end with the -provider syntax. You can see a list of available providers by searching on our GitHub. To add the provider to your package, add it as a dependency in your Package.swift file. let package = Package ( \n name : MyApp , \n dependencies : [ \n . Package ( url : https://github.com/vapor/vapor.git , majorVersion : 2 ), \n . Package ( url : https://github.com/vapor/mysql-provider.git , majorVersion : 2 ) \n ] ) Warning Always run vapor update or vapor clean after editing your Package.swift file.", - "title": "Add Package" - }, - { - "location": "/vapor/provider/#import", - "text": "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: import Vapor import MySQLProvider", - "title": "Import" - }, - { - "location": "/vapor/provider/#add-to-droplet", - "text": "Every provider comes with a class named Provider . Add this class to your Droplet using the addProvider method. let config = try Config () try config . addProvider ( MySQLProvider . Provider . self ) let drop = try Droplet ( config ) // ... drop . run ()", - "title": "Add to Droplet" - }, - { - "location": "/vapor/provider/#configuration", - "text": "Some drivers may require a configuration file. For example, MySQLProvider requires a Config/mysql.json file like the following: { \n hostname : localhost , \n user : root , \n password : , \n database : vapor } You will receive an error during the Droplet 's initialization if a configuration file is required. Tip Storing sensitive configuration files (ones that contain passwords) in the Config/secrets folder will prevent them from being tracked by git.", - "title": "Configuration" - }, - { - "location": "/vapor/provider/#manual", - "text": "Some providers can be configured manually by using the Provider's init method. This method can be used instead of configuration files. let mysqlProvider = VaporMySQL . Provider ( host : localhost , user : root , password : , database : vapor ) try config . addProvider ( mysqlProvider )", - "title": "Manual" - }, - { - "location": "/vapor/provider/#create-a-provider", - "text": "Creating a provider is easy, you just need to create a package with a class Provider that conforms to Vapor.Provider .", - "title": "Create a Provider" - }, - { - "location": "/vapor/provider/#example", - "text": "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. import Vapor public final class Provider : Vapor . Provider { \n public let message : String \n\n public convenience init ( config : Config ) throws { \n guard let message = config [ foo , message ]. string else { \n throw ConfigError . missing ( key : [ message ], file : foo , desiredType : String . self ) \n } \n\n self . init ( message : message ) \n } \n\n public init ( message : String ) { \n self . message = message \n } \n\n public func boot ( _ drop : Droplet ) { } \n\n public func beforeRun ( _ drop : Droplet ) { \n drop . console . info ( message ) \n } } This provider wil require a Config/foo.json file that looks like: { \n message : The message to output } The provider can also be initialized manually with the init(message: String) init.", - "title": "Example" - }, - { - "location": "/vapor/hash/", - "text": "Hash\n\n\nHashing is a one way method of converting arbitrary data into a fixed size format. Unlike ciphers, data that is hashed cannot be retrieved from the resulting digest. Hashes can be used to create keys, file identifiers, or store credentials.\n\n\n\n\n\n\nHash function diagram from \nWikipedia\n.\n\n\n\n\n\n\nWarning\n\n\nAvoid storing password hashes if possible. If you must, please make sure to research the state of the art before continuing.\n\n\n\n\nMake\n\n\nTo hash a string, use the \nhash\n property on \nDroplet\n.\n\n\nlet\n \ndigest\n \n=\n \ntry\n \ndrop\n.\nhash\n.\nmake\n(\nvapor\n)\n\n\nprint\n(\ndigest\n.\nmakeString\n())\n\n\n\n\n\n\nChecking\n\n\nSome hashing algorithms create different hash digests for the same message. Because of this, it is necessary to check your hashes using the \ncheck\n method.\n\n\nlet\n \nmatches\n \n=\n \ntry\n \ndrop\n.\nhash\n.\ncheck\n(\nvapor\n,\n \nmatchesHash\n:\n \ndigest\n)\n\n\n\n\n\n\nCryptoHasher\n\n\nBy default, Vapor uses a SHA-256 hasher. You can change this in the configuration files or by giving the \nDroplet\n a different hasher.\n\n\nConfiguration\n\n\nConfig/droplet.json\n\n\n{\n\n \n...,\n\n \nhash\n:\n \ncrypto\n,\n\n \n...\n\n\n}\n\n\n\n\n\n\nConfig/crypto.json\n\n\n{\n\n \nhash\n:\n \n{\n\n \nmethod\n:\n \nsha256\n,\n\n \nencoding\n:\n \nhex\n,\n\n \nkey\n:\n \npassword\n\n \n},\n\n \n...\n\n\n}\n\n\n\n\n\n\nEncoding\n\n\nThe CryptoHasher supports three methods of encoding.\n\n\n\n\nhex\n\n\nbase64\n\n\nplain\n\n\n\n\nKey\n\n\nSupplying a key will cause the hasher to produce \nkeyed\n hashes using HMAC. Some hashers require a key.\n\n\nSupported\n\n\n\n\n\n\n\n\nName\n\n\nMethod\n\n\nRequires Key\n\n\n\n\n\n\n\n\n\n\nSHA-1\n\n\nsha1\n\n\nno\n\n\n\n\n\n\nSHA-224\n\n\nsha224\n\n\nno\n\n\n\n\n\n\nSHA-256\n\n\nsha256\n\n\nno\n\n\n\n\n\n\nSHA-384\n\n\nsha384\n\n\nno\n\n\n\n\n\n\nSHA-512\n\n\nsha512\n\n\nno\n\n\n\n\n\n\nMD4\n\n\nmd4\n\n\nno\n\n\n\n\n\n\nMD5\n\n\nmd5\n\n\nno\n\n\n\n\n\n\nRIPEMD-160\n\n\nripemd160\n\n\nno\n\n\n\n\n\n\nWhirlpool\n\n\nwhirlpool\n\n\nyes\n\n\n\n\n\n\nStreebog-256\n\n\nstreebog256\n\n\nyes\n\n\n\n\n\n\nStreebog-512\n\n\nstreebog512\n\n\nyes\n\n\n\n\n\n\nGostR341194\n\n\ngostr341194\n\n\nyes\n\n\n\n\n\n\n\n\nManual\n\n\nHashers can be swapped without the use of configuration files.\n\n\nHash\n\n\nlet\n \nhash\n \n=\n \nCryptoHasher\n(\n\n \nhash\n:\n \n.\nsha256\n,\n\n \nencoding\n:\n \n.\nhex\n\n\n)\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nhash\n:\n \nhash\n)\n\n\n\n\n\n\nHMAC\n\n\nlet\n \nhash\n \n=\n \nCryptoHasher\n(\n\n \nhmac\n:\n \n.\nsha256\n,\n\n \nencoding\n:\n \n.\nhex\n,\n\n \nkey\n:\n \npassword\n.\nmakeBytes\n()\n\n\n)\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nhash\n:\n \nhash\n)\n\n\n\n\n\n\nBCryptHasher\n\n\nBCrypt is a password hashing function that automatically incorporates salts and offers a configurable cost. The cost can be used to increase the computation required to generate a hash.\n\n\n\n\nSeealso\n\n\nLearn more about \nkey stretching\n on Wikipedia.\n\n\n\n\nConfiguration\n\n\nTo use the BCryptHasher change the \n\"hash\"\n key in the \ndroplet.json\n configuration file.\n\n\nConfig/droplet.json\n\n\n{\n\n \n...,\n\n \nhash\n:\n \nbcrypt\n,\n\n \n...\n\n\n}\n\n\n\n\n\n\nTo configure the cost, add a \nbcrypt.json\n file.\n\n\nConfig/bcrypt.json\n\n\n{\n\n \ncost\n:\n \n8\n\n\n}\n\n\n\n\n\n\n\n\nTip\n\n\nYou can use different BCrypt costs for production and development modes to keep password hashing fast while working on a project.\n\n\n\n\nManual\n\n\nYou can manually assign a \nBCryptHasher\n to \ndrop.hash\n.\n\n\nlet\n \nhash\n \n=\n \nBCryptHasher\n(\ncost\n:\n \n8\n)\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nhash\n:\n \nhash\n)\n\n\n\n\n\n\nAdvanced\n\n\nCustom\n\n\nYou can also create your own hasher. You just need to conform to the \nHash\n protocol.\n\n\n/// Creates hash digests\n\n\npublic\n \nprotocol\n \nHashProtocol\n \n{\n\n \n/// Given a message, this method\n\n \n/// returns a hashed digest of that message.\n\n \nfunc\n \nmake\n(\n_\n \nmessage\n:\n \nBytes\n)\n \nthrows\n \n-\n \nBytes\n\n\n \n/// Checks whether a given digest was created\n\n \n/// by the supplied message.\n\n \n///\n\n \n/// Returns true if the digest was created\n\n \n/// by the supplied message, false otherwise.\n\n \nfunc\n \ncheck\n(\n_\n \nmessage\n:\n \nBytes\n,\n \nmatchesHash\n:\n \nBytes\n)\n \nthrows\n \n-\n \nBool\n\n\n}", - "title": "Hash" - }, - { - "location": "/vapor/hash/#hash", - "text": "Hashing is a one way method of converting arbitrary data into a fixed size format. Unlike ciphers, data that is hashed cannot be retrieved from the resulting digest. Hashes can be used to create keys, file identifiers, or store credentials. Hash function diagram from Wikipedia . Warning Avoid storing password hashes if possible. If you must, please make sure to research the state of the art before continuing.", - "title": "Hash" - }, - { - "location": "/vapor/hash/#make", - "text": "To hash a string, use the hash property on Droplet . let digest = try drop . hash . make ( vapor ) print ( digest . makeString ())", - "title": "Make" - }, - { - "location": "/vapor/hash/#checking", - "text": "Some hashing algorithms create different hash digests for the same message. Because of this, it is necessary to check your hashes using the check method. let matches = try drop . hash . check ( vapor , matchesHash : digest )", - "title": "Checking" - }, - { - "location": "/vapor/hash/#cryptohasher", - "text": "By default, Vapor uses a SHA-256 hasher. You can change this in the configuration files or by giving the Droplet a different hasher.", - "title": "CryptoHasher" - }, - { - "location": "/vapor/hash/#configuration", - "text": "Config/droplet.json { \n ..., \n hash : crypto , \n ... } Config/crypto.json { \n hash : { \n method : sha256 , \n encoding : hex , \n key : password \n }, \n ... }", - "title": "Configuration" - }, - { - "location": "/vapor/hash/#encoding", - "text": "The CryptoHasher supports three methods of encoding. hex base64 plain", - "title": "Encoding" - }, - { - "location": "/vapor/hash/#key", - "text": "Supplying a key will cause the hasher to produce keyed hashes using HMAC. Some hashers require a key.", - "title": "Key" - }, - { - "location": "/vapor/hash/#supported", - "text": "Name Method Requires Key SHA-1 sha1 no SHA-224 sha224 no SHA-256 sha256 no SHA-384 sha384 no SHA-512 sha512 no MD4 md4 no MD5 md5 no RIPEMD-160 ripemd160 no Whirlpool whirlpool yes Streebog-256 streebog256 yes Streebog-512 streebog512 yes GostR341194 gostr341194 yes", - "title": "Supported" - }, - { - "location": "/vapor/hash/#manual", - "text": "Hashers can be swapped without the use of configuration files.", - "title": "Manual" - }, - { - "location": "/vapor/hash/#hash_1", - "text": "let hash = CryptoHasher ( \n hash : . sha256 , \n encoding : . hex ) let drop = try Droplet ( hash : hash )", - "title": "Hash" - }, - { - "location": "/vapor/hash/#hmac", - "text": "let hash = CryptoHasher ( \n hmac : . sha256 , \n encoding : . hex , \n key : password . makeBytes () ) let drop = try Droplet ( hash : hash )", - "title": "HMAC" - }, - { - "location": "/vapor/hash/#bcrypthasher", - "text": "BCrypt is a password hashing function that automatically incorporates salts and offers a configurable cost. The cost can be used to increase the computation required to generate a hash. Seealso Learn more about key stretching on Wikipedia.", - "title": "BCryptHasher" - }, - { - "location": "/vapor/hash/#configuration_1", - "text": "To use the BCryptHasher change the \"hash\" key in the droplet.json configuration file. Config/droplet.json { \n ..., \n hash : bcrypt , \n ... } To configure the cost, add a bcrypt.json file. Config/bcrypt.json { \n cost : 8 } Tip You can use different BCrypt costs for production and development modes to keep password hashing fast while working on a project.", - "title": "Configuration" - }, - { - "location": "/vapor/hash/#manual_1", - "text": "You can manually assign a BCryptHasher to drop.hash . let hash = BCryptHasher ( cost : 8 ) let drop = try Droplet ( hash : hash )", - "title": "Manual" - }, - { - "location": "/vapor/hash/#advanced", - "text": "", - "title": "Advanced" - }, - { - "location": "/vapor/hash/#custom", - "text": "You can also create your own hasher. You just need to conform to the Hash protocol. /// Creates hash digests public protocol HashProtocol { \n /// Given a message, this method \n /// returns a hashed digest of that message. \n func make ( _ message : Bytes ) throws - Bytes \n\n /// Checks whether a given digest was created \n /// by the supplied message. \n /// \n /// Returns true if the digest was created \n /// by the supplied message, false otherwise. \n func check ( _ message : Bytes , matchesHash : Bytes ) throws - Bool }", - "title": "Custom" - }, - { - "location": "/vapor/log/", - "text": "Log information using \ndrop.log\n.\n\n\ndrop\n.\nlog\n.\ninfo\n(\nInformational log\n)\n\n\n\n\n\n\nTypes\n\n\nBelow are the following methods you can call on the log protocol. Only \nerror\n and \nfatal\n will be shown in \nproduction\n mode.\n\n\n\n\n\n\n\n\nMethod\n\n\nProduction\n\n\n\n\n\n\n\n\n\n\ninfo\n\n\nNo\n\n\n\n\n\n\nwarning\n\n\nNo\n\n\n\n\n\n\nverbose\n\n\nNo\n\n\n\n\n\n\ndebug\n\n\nNo\n\n\n\n\n\n\nerror\n\n\nYes\n\n\n\n\n\n\nfatal\n\n\nYes\n\n\n\n\n\n\n\n\nProtocol\n\n\nCreate your own logger by conforming to \nLogProtocol\n. \n\n\n/// Logger protocol. Custom loggers must conform\n\n\n/// to this protocol\n\n\npublic\n \nprotocol\n \nLogProtocol\n:\n \nclass\n \n{\n\n \n/// Enabled log levels. Only levels in this\n\n \n/// array should be logged.\n\n \nvar\n \nenabled\n:\n \n[\nLogLevel\n]\n \n{\n \nget\n \nset\n \n}\n\n\n \n/// Log the given message at the passed filter level.\n\n \n/// file, function and line of the logging call\n\n \n/// are automatically injected in the convenience function.\n\n \nfunc\n \nlog\n(\n_\n \nlevel\n:\n \nLogLevel\n,\n \nmessage\n:\n \nString\n,\n \nfile\n:\n \nString\n,\n \nfunction\n:\n \nString\n,\n \nline\n:\n \nInt\n)\n\n\n}", - "title": "Log" - }, - { - "location": "/vapor/log/#types", - "text": "Below are the following methods you can call on the log protocol. Only error and fatal will be shown in production mode. Method Production info No warning No verbose No debug No error Yes fatal Yes", - "title": "Types" - }, - { - "location": "/vapor/log/#protocol", - "text": "Create your own logger by conforming to LogProtocol . /// Logger protocol. Custom loggers must conform /// to this protocol public protocol LogProtocol : class { \n /// Enabled log levels. Only levels in this \n /// array should be logged. \n var enabled : [ LogLevel ] { get set } \n\n /// Log the given message at the passed filter level. \n /// file, function and line of the logging call \n /// are automatically injected in the convenience function. \n func log ( _ level : LogLevel , message : String , file : String , function : String , line : Int ) }", - "title": "Protocol" - }, - { - "location": "/vapor/commands/", - "text": "In addition to the commands provided by Vapor (like \nserve\n, and \nroutes\n) you can build your own custom commands.\n\n\n\n\nNote\n\n\nCommands are a great way to script your application with CRON jobs.\n\n\n\n\nExample\n\n\nTo make a custom console command we must first create a new \n.swift\n file, import \nVapor\n and \nConsole\n, and implement the \nCommand\n protocol.\n\n\nimport\n \nVapor\n\n\nimport\n \nConsole\n\n\n\nfinal\n \nclass\n \nMyCustomCommand\n:\n \nCommand\n \n{\n\n \npublic\n \nlet\n \nid\n \n=\n \nmy-command\n\n \npublic\n \nlet\n \nhelp\n \n=\n \n[\nThis command does things, like foo, and bar.\n]\n\n \npublic\n \nlet\n \nconsole\n:\n \nConsoleProtocol\n\n\n \npublic\n \ninit\n(\nconsole\n:\n \nConsoleProtocol\n)\n \n{\n\n \nself\n.\nconsole\n \n=\n \nconsole\n\n \n}\n\n\n \npublic\n \nfunc\n \nrun\n(\narguments\n:\n \n[\nString\n])\n \nthrows\n \n{\n\n \nconsole\n.\nprint\n(\nrunning custom command...\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\n\n\nThe \nid\n property is the string you will type in the console to access the command. \n.build/debug/App command\n will run the Custom Command.\n\n\nThe \nhelp\n property is the help message that will give your custom command's users some idea of how to access it.\n\n\nThe \nconsole\n property is the object passed to your custom command that adheres to the console protocol, allowing manipulation of the console.\n\n\nThe \nrun\n method is where you put the logic relating to your command.\n\n\n\n\nConfig Initializable\n\n\nTo make our command configurable, conform it to \nConfigInitializable\n\n\nextension\n \nMyCustomCommand\n:\n \nConfigInitializable\n \n{\n\n \npublic\n \nconvenience\n \ninit\n(\nconfig\n:\n \nConfig\n)\n \nthrows\n \n{\n\n \nlet\n \nconsole\n \n=\n \ntry\n \nconfig\n.\nresolveConsole\n()\n\n \nself\n.\ninit\n(\nconsole\n:\n \nconsole\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nAdd to Droplet\n\n\nAfter we work our magic in the Custom Command file, we switch over to our \nmain.swift\n file and add the custom command to the Droplet like so.\n\n\nimport\n \nVapor\n\n\n\nlet\n \nconfig\n \n=\n \ntry\n \nConfig\n()\n\n\ntry\n \nconfig\n.\naddConfigurable\n(\ncommand\n:\n \nMyCustomCommand\n.\ninit\n,\n \nname\n:\n \nmy-command\n)\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nconfig\n)\n\n\n\n\n\n\nThis allows Vapor access to our custom command and lets it know to display it in the \n--help\n section of the program.\n\n\nConfigure\n\n\nNow that you've made the command configurable, just add it to the \ncommands\n array in your \nConfig/droplet.json\n file.\n\n\nConfig/droplet.json\n\n\n{\n\n \n...,\n\n \ncommands\n:\n \n[\nmy-command\n],\n\n \n...,\n\n\n}\n\n\n\n\n\n\nAfter compiling the application we can run our custom command like so.\n\n\nvapor run my-command", - "title": "Commands" - }, - { - "location": "/vapor/commands/#example", - "text": "To make a custom console command we must first create a new .swift file, import Vapor and Console , and implement the Command protocol. import Vapor import Console final class MyCustomCommand : Command { \n public let id = my-command \n public let help = [ This command does things, like foo, and bar. ] \n public let console : ConsoleProtocol \n\n public init ( console : ConsoleProtocol ) { \n self . console = console \n } \n\n public func run ( arguments : [ String ]) throws { \n console . print ( running custom command... ) \n } } 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.", - "title": "Example" - }, - { - "location": "/vapor/commands/#config-initializable", - "text": "To make our command configurable, conform it to ConfigInitializable extension MyCustomCommand : ConfigInitializable { \n public convenience init ( config : Config ) throws { \n let console = try config . resolveConsole () \n self . init ( console : console ) \n } }", - "title": "Config Initializable" - }, - { - "location": "/vapor/commands/#add-to-droplet", - "text": "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. import Vapor let config = try Config () try config . addConfigurable ( command : MyCustomCommand . init , name : my-command ) let drop = try Droplet ( config ) This allows Vapor access to our custom command and lets it know to display it in the --help section of the program.", - "title": "Add to Droplet" - }, - { - "location": "/vapor/commands/#configure", - "text": "Now that you've made the command configurable, just add it to the commands array in your Config/droplet.json file. Config/droplet.json { \n ..., \n commands : [ my-command ], \n ..., } After compiling the application we can run our custom command like so. vapor run my-command", - "title": "Configure" - }, - { - "location": "/configs/config/", - "text": "Config\n\n\nAn 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.\n\n\nQuickStart\n\n\nFor Vapor applications, configuration files are expected to be nested under a top level folder named \nConfig\n. Here's an example of a basic config featuring a single \nservers\n configuration.\n\n\n./\n\u251c\u2500\u2500 Config/\n\u2502 \u251c\u2500\u2500 server.json\n\n\n\n\n\nAnd an example of how this might look:\n\n\n{\n\n \nhost\n:\n \n0.0.0.0\n,\n\n \nport\n:\n \n8080\n,\n\n \nsecurityLayer\n:\n \nnone\n\n\n}\n\n\n\n\n\n\nWhat that's saying, is that our application should start a server on port \n8080\n and host \n0.0.0.0\n. This represents the following url: \nhttp://localhost:8080\n.\n\n\nCustom Keys\n\n\nLet's add a custom key to the \nserver.json\n file:\n\n\n{\n\n \nhost\n:\n \n0.0.0.0\n,\n\n \nport\n:\n \n8080\n,\n\n \nsecurityLayer\n:\n \nnone\n,\n\n \ncustom-key\n:\n \ncustom value\n\n\n}\n\n\n\n\n\n\nThis can be accessed from your application's config using the following.\n\n\nlet\n \ncustomValue\n \n=\n \ndrop\n.\nconfig\n[\nserver\n,\n \ncustom-key\n]?.\nstring\n \n??\n \ndefault\n\n\n\n\n\n\nThat's it, feel free to add and utilize keys as necessary to make your application configuration easier.\n\n\nConfig Syntax\n\n\nYou can access your config directory with the following syntax. \napp.config[fileName, path, to, key]\n. For example, let's hypothesize that in addition to the \nserver.json\n file we mentioned earlier, there is also a \nkeys.json\n that looks like this:\n\n\n{\n\n \ntest-names\n:\n \n[\n\n \njoe\n,\n\n \njane\n,\n\n \nsara\n\n \n],\n\n \nmongo\n:\n \n{\n\n \nurl\n \n:\n \nwww.customMongoUrl.com\n\n \n}\n\n\n}\n\n\n\n\n\n\nWe can access this file by making sure the first argument in our subscript is keys. To get the first name in our list:\n\n\nlet\n \nname\n \n=\n \ndrop\n.\nconfig\n[\nkeys\n,\n \ntest-names\n,\n \n0\n]?.\nstring\n \n??\n \ndefault\n\n\n\n\n\n\nOr our mongo url:\n\n\nlet\n \nmongoUrl\n \n=\n \ndrop\n.\nconfig\n[\nkeys\n,\n \nmongo\n,\n \nurl\n]?.\nstring\n \n??\n \ndefault\n\n\n\n\n\n\nAdvanced Configurations\n\n\nHaving the default \nserver.json\n 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 \nConfig/\n directory. Here's an example of a folder structure that's setup for production and development environments.\n\n\nWorkingDirectory/\n\u251c\u2500\u2500 Config/\n\u2502 \u251c\u2500\u2500 server.json\n\u2502 \u251c\u2500\u2500 production/\n\u2502 \u2502 \u2514\u2500\u2500 server.json\n\u2502 \u251c\u2500\u2500 development/\n\u2502 \u2502 \u2514\u2500\u2500 server.json\n\u2502 \u2514\u2500\u2500 secrets/\n\u2502 \u2514\u2500\u2500 server.json\n\n\n\n\n\n\n\nYou 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.\n\n\n\n\nvapor run --env\n=\nproduction\n\n\n\n\n\nPRIORITY\n\n\nConfig files will be accessed in the following priority.\n\n\n\n\nCLI (see below)\n\n\nConfig/secrets/\n\n\nConfig/name-of-environment/\n\n\nConfig/\n\n\n\n\nWhat this means is that if a user calls \napp.config[\"server\", \"host\"]\n, the key will be searched in the CLI first, then the \nsecrets/\n directory, then the top level default configs.\n\n\n\n\nsecrets/\n directory should very likely be added to the gitignore.\n\n\n\n\nEXAMPLE\n\n\nLet's start with the following JSON files.\n\n\nserver.json\n\n\n{\n\n \nhost\n:\n \n0.0.0.0\n,\n\n \nport\n:\n \n9000\n\n\n}\n\n\n\n\n\n\nproduction/server.json\n\n\n{\n\n \nhost\n:\n \n127.0.0.1\n,\n\n \nport\n:\n \n$PORT\n\n\n}\n\n\n\n\n\n\n\n\nThe \n\"$NAME\"\n syntax is available for all values to access environment variables.\n\n\n\n\nPlease notice that \nserver.json\n, and \nproduction/server.json\n both declare the same keys: \nhost\n, and \nport\n. In our application, we'll call:\n\n\n// will load 0.0.0.0 or 127.0.0.1 based on above config\n\n\nlet\n \nhost\n \n=\n \ndrop\n.\nconfig\n[\nserver\n \nhost\n]?.\nstring\n \n??\n \n0.0.0.0\n\n\n// will load 9000, or environment variable port.\n\n\nlet\n \nport\n \n=\n \ndrop\n.\nconfig\n[\nserver\n,\n \nport\n]?.\nint\n \n??\n \n9000\n\n\n\n\n\n\nConfiguration file Options\n\n\ndroplet.json\n\n\n{\n\n \nserver\n:\n \nengine\n,\n\n \nclient\n:\n \nengine\n,\n\n \nconsole\n:\n \nterminal\n,\n\n \nlog\n:\n \nconsole\n,\n\n \nhash\n:\n \ncrypto\n,\n\n \ncipher\n:\n \ncrypto\n,\n\n \nmiddleware\n:\n \n[\n\n \nerror\n,\n\n \ndate\n,\n\n \nfile\n\n \n],\n\n \ncommands\n:\n \n[\n\n \nprepare\n\n \n]\n\n\n}\n\n\n\n\n\n\nserver.json\n\n\n{\n\n \nport\n:\n \n$PORT:8080\n,\n\n \nhost\n:\n \n0.0.0.0\n,\n\n \nsecurityLayer\n:\n \nnone\n\n\n}\n\n\n\n\n\n\nfluent.json\n\n\n{\n\n \ndriver\n:\n \nmemory\n,\n\n \nkeyNamingConvention\n:\n \nsnake_case\n,\n\n \nmigrationEntityName\n:\n \nfluent\n,\n\n \npivotNameConnector\n:\n \n_\n,\n\n \nautoForeignKeys\n:\n \ntrue\n,\n\n \ndefaultPageKey\n:\n \npage\n,\n\n \ndefaultPageSize\n:\n \n10\n,\n\n \nlog\n:\n \nfalse\n,\n \n \nmaxConnections\n:\n10\n\n\n}\n\n\n\n\n\n\ncrypto.json\n\n\n{\n\n \nhash\n:\n \n{\n\n \nmethod\n:\n \nsha256\n,\n\n \nencoding\n:\n \nhex\n,\n\n \nkey\n:\n \n0000000000000000\n\n \n},\n\n\n \ncipher\n:\n \n{\n\n \nmethod\n:\n \naes256\n,\n\n \nencoding\n:\n \nbase64\n,\n\n \nkey\n:\n \nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n\n \n}\n\n\n}\n\n\n\n\n\n\nCOMMAND LINE\n\n\nIn addition to json files nested within the \nConfig/\n 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.\n\n\nIf you want command line arguments set to a file besides \"cli\", you can use this more advanced specification. For example, the following CLI command:\n\n\n--config:keys.analytics\n=\n124ZH61F\n\n\n\n\n\nwould be accessible within your application by using the following:\n\n\nlet\n \nanalyticsKey\n \n=\n \ndrop\n.\nconfig\n[\nkeys\n,\n \nanalytics\n]?.\nstring", - "title": "Config" - }, - { - "location": "/configs/config/#config", - "text": "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.", - "title": "Config" - }, - { - "location": "/configs/config/#quickstart", - "text": "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. ./\n\u251c\u2500\u2500 Config/\n\u2502 \u251c\u2500\u2500 server.json And an example of how this might look: { \n host : 0.0.0.0 , \n port : 8080 , \n 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 .", - "title": "QuickStart" - }, - { - "location": "/configs/config/#custom-keys", - "text": "Let's add a custom key to the server.json file: { \n host : 0.0.0.0 , \n port : 8080 , \n securityLayer : none , \n custom-key : custom value } This can be accessed from your application's config using the following. 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.", - "title": "Custom Keys" - }, - { - "location": "/configs/config/#config-syntax", - "text": "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: { \n test-names : [ \n joe , \n jane , \n sara \n ], \n mongo : { \n url : www.customMongoUrl.com \n } } We can access this file by making sure the first argument in our subscript is keys. To get the first name in our list: let name = drop . config [ keys , test-names , 0 ]?. string ?? default Or our mongo url: let mongoUrl = drop . config [ keys , mongo , url ]?. string ?? default", - "title": "Config Syntax" - }, - { - "location": "/configs/config/#advanced-configurations", - "text": "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. WorkingDirectory/\n\u251c\u2500\u2500 Config/\n\u2502 \u251c\u2500\u2500 server.json\n\u2502 \u251c\u2500\u2500 production/\n\u2502 \u2502 \u2514\u2500\u2500 server.json\n\u2502 \u251c\u2500\u2500 development/\n\u2502 \u2502 \u2514\u2500\u2500 server.json\n\u2502 \u2514\u2500\u2500 secrets/\n\u2502 \u2514\u2500\u2500 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. vapor run --env = production", - "title": "Advanced Configurations" - }, - { - "location": "/configs/config/#priority", - "text": "Config files will be accessed in the following priority. CLI (see below) Config/secrets/ Config/name-of-environment/ 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.", - "title": "PRIORITY" - }, - { - "location": "/configs/config/#example", - "text": "Let's start with the following JSON files.", - "title": "EXAMPLE" - }, - { - "location": "/configs/config/#serverjson", - "text": "{ \n host : 0.0.0.0 , \n port : 9000 }", - "title": "server.json" - }, - { - "location": "/configs/config/#productionserverjson", - "text": "{ \n host : 127.0.0.1 , \n 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: // 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", - "title": "production/server.json" - }, - { - "location": "/configs/config/#configuration-file-options", - "text": "", - "title": "Configuration file Options" - }, - { - "location": "/configs/config/#dropletjson", - "text": "{ \n server : engine , \n client : engine , \n console : terminal , \n log : console , \n hash : crypto , \n cipher : crypto , \n middleware : [ \n error , \n date , \n file \n ], \n commands : [ \n prepare \n ] }", - "title": "droplet.json" - }, - { - "location": "/configs/config/#serverjson_1", - "text": "{ \n port : $PORT:8080 , \n host : 0.0.0.0 , \n securityLayer : none }", - "title": "server.json" - }, - { - "location": "/configs/config/#fluentjson", - "text": "{ \n driver : memory , \n keyNamingConvention : snake_case , \n migrationEntityName : fluent , \n pivotNameConnector : _ , \n autoForeignKeys : true , \n defaultPageKey : page , \n defaultPageSize : 10 , \n log : false , \n maxConnections : 10 }", - "title": "fluent.json" - }, - { - "location": "/configs/config/#cryptojson", - "text": "{ \n hash : { \n method : sha256 , \n encoding : hex , \n key : 0000000000000000 \n }, \n\n cipher : { \n method : aes256 , \n encoding : base64 , \n key : AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= \n } }", - "title": "crypto.json" - }, - { - "location": "/configs/config/#command-line", - "text": "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: --config:keys.analytics = 124ZH61F would be accessible within your application by using the following: let analyticsKey = drop . config [ keys , analytics ]?. string", - "title": "COMMAND LINE" - }, - { - "location": "/json/package/", - "text": "Using JSON\n\n\nWith Vapor\n\n\nThis package is included with Vapor by default, just add:\n\n\nimport\n \nJSON\n\n\n\n\n\n\nWithout Vapor\n\n\nJSON provides easy-to-use JSON support for any server-side, or client side Swift project. To include it in your package, add the following to your \nPackage.swift\n file.\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n...\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/json.git\n,\n \nmajorVersion\n:\n \n2\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport JSON\n to access JSON's APIs", - "title": "Package" - }, - { - "location": "/json/package/#using-json", - "text": "", - "title": "Using JSON" - }, - { - "location": "/json/package/#with-vapor", - "text": "This package is included with Vapor by default, just add: import JSON", - "title": "With Vapor" - }, - { - "location": "/json/package/#without-vapor", - "text": "JSON provides easy-to-use JSON support for any server-side, or client side Swift project. To include it in your package, add the following to your Package.swift file. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . Package ( url : https://github.com/vapor/json.git , majorVersion : 2 ) \n ], \n exclude : [ ... ] ) Use import JSON to access JSON's APIs", - "title": "Without Vapor" - }, - { - "location": "/json/overview/", - "text": "JSON\n\n\nJSON is an integral part of Vapor. It powers Vapor's \nConfig\n and is easy to use in both requests and responses.\n\n\nRequest\n\n\nJSON is automatically available in \nrequest.data\n alongside Form URL Encoded, Form 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.\n\n\ndrop\n.\nget\n(\nhello\n)\n \n{\n \nrequest\n \nin\n\n \nguard\n \nlet\n \nname\n \n=\n \nrequest\n.\ndata\n[\nname\n]?.\nstring\n \nelse\n \n{\n\n \nthrow\n \nAbort\n(.\nbadRequest\n)\n\n \n}\n\n \nreturn\n \nHello, \n\\(\nname\n)\n!\n\n\n}\n\n\n\n\n\n\nThis will return a greeting for any HTTP method or content type that the \nname\n is sent as, including JSON.\n\n\nJSON Only\n\n\nTo specifically target JSON, use the \nrequest.json\n property.\n\n\ndrop\n.\npost\n(\njson\n)\n \n{\n \nrequest\n \nin\n\n \nguard\n \nlet\n \nname\n \n=\n \nrequest\n.\njson\n?[\nname\n]?.\nstring\n \nelse\n \n{\n\n \nthrow\n \nAbort\n(.\nbadRequest\n)\n\n \n}\n\n\n \nreturn\n \nHello, \n\\(\nname\n)\n!\n\n\n}\n\n\n\n\n\n\nThe above snippet will only work if the request is sent with JSON data.\n\n\nResponse\n\n\nTo respond with JSON, simply create a \nJSON\n object and add values to it.\n\n\ndrop\n.\nget\n(\nversion\n)\n \n{\n \nrequest\n \nin\n\n \nvar\n \njson\n \n=\n \nJSON\n()\n\n \ntry\n \njson\n.\nset\n(\nversion\n,\n \n1.0\n)\n\n \nreturn\n \njson\n\n\n}\n\n\n\n\n\n\nConvertible\n\n\nMaking your classes and structs JSON convertible is a great way to interact with APIs in an organized and DRY way.\n\n\nRepresentable\n\n\nWhen something conforms to \nJSONRepresentable\n, it can be converted into JSON.\n\n\nextension\n \nUser\n:\n \nJSONRepresentable\n \n{\n\n \nfunc\n \nmakeJSON\n()\n \nthrows\n \n-\n \nJSON\n \n{\n\n \nvar\n \njson\n \n=\n \nJSON\n()\n\n \ntry\n \njson\n.\nset\n(\nid\n,\n \nid\n)\n\n \ntry\n \njson\n.\nset\n(\nname\n,\n \nname\n)\n\n \ntry\n \njson\n.\nset\n(\nage\n,\n \nage\n)\n\n \nreturn\n \njson\n\n \n}\n\n\n}\n\n\n\n\n\n\nNow you can simply return \nuser.makeJSON()\n in your routes.\n\n\ndrop\n.\nget\n(\nusers\n,\n \nUser\n.\nparameter\n)\n \n{\n \nreq\n \nin\n\n \nlet\n \nuser\n \n=\n \ntry\n \nreq\n.\nparameters\n.\nnext\n(\nUser\n.\nself\n)\n\n \nreturn\n \ntry\n \nuser\n.\nmakeJSON\n()\n\n\n}\n\n\n\n\n\n\nYou can even go a step further and conform your model to \nResponseRepresentable\n. Since it's already \nJSONRepresentable\n\nyou will get the conformance for free.\n\n\nextension\n \nUser\n:\n \nResponseRepresentable\n \n{\n \n}\n\n\n\n\n\n\nNow you can return the model by itself. It will automatically call \n.makeJSON()\n.\n\n\ndrop\n.\nget\n(\nusers\n,\n \nUser\n.\nparameter\n)\n \n{\n \nreq\n \nin\n\n \nlet\n \nuser\n \n=\n \ntry\n \nreq\n.\nparameters\n.\nnext\n(\nUser\n.\nself\n)\n\n \nreturn\n \ntry\n \nuser\n\n\n}\n\n\n\n\n\n\nInitializable\n\n\nWhen something conforms to \nJSONInitializable\n, it can be created from JSON.\n\n\nextension\n \nUser\n:\n \nJSONInitializable\n \n{\n\n \nconvenience\n \ninit\n(\njson\n:\n \nJSON\n)\n \nthrows\n \n{\n\n \ntry\n \nself\n.\ninit\n(\n\n \nname\n:\n \njson\n.\nget\n(\nname\n),\n\n \nage\n:\n \njson\n.\nget\n(\nage\n)\n\n \n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nNow you can simply call \nUser(json: ...)\n to create a user from JSON.\n\n\ndrop\n.\npost\n(\nusers\n)\n \n{\n \nreq\n \nin\n\n \nguard\n \nlet\n \njson\n \n=\n \nreq\n.\njson\n \nelse\n \n{\n\n \nthrow\n \nAbort\n(.\nbadRequest\n)\n\n \n}\n\n\n \nlet\n \nuser\n \n=\n \ntry\n \nUser\n(\njson\n:\n \njson\n)\n\n \ntry\n \nuser\n.\nsave\n()\n\n \nreturn\n \nuser\n\n\n}", - "title": "Overview" - }, - { - "location": "/json/overview/#json", - "text": "JSON is an integral part of Vapor. It powers Vapor's Config and is easy to use in both requests and responses.", - "title": "JSON" - }, - { - "location": "/json/overview/#request", - "text": "JSON is automatically available in request.data alongside Form URL Encoded, Form 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. drop . get ( hello ) { request in \n guard let name = request . data [ name ]?. string else { \n throw Abort (. badRequest ) \n } \n return Hello, \\( name ) ! } This will return a greeting for any HTTP method or content type that the name is sent as, including JSON.", - "title": "Request" - }, - { - "location": "/json/overview/#json-only", - "text": "To specifically target JSON, use the request.json property. drop . post ( json ) { request in \n guard let name = request . json ?[ name ]?. string else { \n throw Abort (. badRequest ) \n } \n\n return Hello, \\( name ) ! } The above snippet will only work if the request is sent with JSON data.", - "title": "JSON Only" - }, - { - "location": "/json/overview/#response", - "text": "To respond with JSON, simply create a JSON object and add values to it. drop . get ( version ) { request in \n var json = JSON () \n try json . set ( version , 1.0 ) \n return json }", - "title": "Response" - }, - { - "location": "/json/overview/#convertible", - "text": "Making your classes and structs JSON convertible is a great way to interact with APIs in an organized and DRY way.", - "title": "Convertible" - }, - { - "location": "/json/overview/#representable", - "text": "When something conforms to JSONRepresentable , it can be converted into JSON. extension User : JSONRepresentable { \n func makeJSON () throws - JSON { \n var json = JSON () \n try json . set ( id , id ) \n try json . set ( name , name ) \n try json . set ( age , age ) \n return json \n } } Now you can simply return user.makeJSON() in your routes. drop . get ( users , User . parameter ) { req in \n let user = try req . parameters . next ( User . self ) \n return try user . makeJSON () } You can even go a step further and conform your model to ResponseRepresentable . Since it's already JSONRepresentable \nyou will get the conformance for free. extension User : ResponseRepresentable { } Now you can return the model by itself. It will automatically call .makeJSON() . drop . get ( users , User . parameter ) { req in \n let user = try req . parameters . next ( User . self ) \n return try user }", - "title": "Representable" - }, - { - "location": "/json/overview/#initializable", - "text": "When something conforms to JSONInitializable , it can be created from JSON. extension User : JSONInitializable { \n convenience init ( json : JSON ) throws { \n try self . init ( \n name : json . get ( name ), \n age : json . get ( age ) \n ) \n } } Now you can simply call User(json: ...) to create a user from JSON. drop . post ( users ) { req in \n guard let json = req . json else { \n throw Abort (. badRequest ) \n } \n\n let user = try User ( json : json ) \n try user . save () \n return user }", - "title": "Initializable" - }, - { - "location": "/routing/package/", - "text": "Using Routing\n\n\nWith Vapor\n\n\nThis package is included with Vapor by default, just add:\n\n\nimport\n \nRouting\n\n\n\n\n\n\nWithout Vapor\n\n\nRouting providers a performant, pure-Swift router to use in any server-side Swift project. To include it in your package, add the following to your \nPackage.swift\n file.\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n...\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/routing.git\n,\n \nmajorVersion\n:\n \n2\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Routing\n to access Routing's APIs", - "title": "Package" - }, - { - "location": "/routing/package/#using-routing", - "text": "", - "title": "Using Routing" - }, - { - "location": "/routing/package/#with-vapor", - "text": "This package is included with Vapor by default, just add: import Routing", - "title": "With Vapor" - }, - { - "location": "/routing/package/#without-vapor", - "text": "Routing providers a performant, pure-Swift router to use in any server-side Swift project. To include it in your package, add the following to your Package.swift file. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . Package ( url : https://github.com/vapor/routing.git , majorVersion : 2 ) \n ], \n exclude : [ ... ] ) Use import Routing to access Routing's APIs", - "title": "Without Vapor" - }, - { - "location": "/routing/overview/", - "text": "Basic Routing\n\n\nRouting is one of the most critical parts of a web framework. The router decides which requests get which responses.\n\n\nVapor has a plethora of functionality for routing including route builders, groups, and collections. In this section, we will look at the basics of routing.\n\n\nRegister\n\n\nThe most basic route includes a method, path, and closure.\n\n\ndrop\n.\nget\n(\nwelcome\n)\n \n{\n \nrequest\n \nin\n\n \nreturn\n \nHello\n\n\n}\n\n\n\n\n\n\nThe standard HTTP methods are available including \nget\n, \npost\n, \nput\n, \npatch\n, \ndelete\n, and \noptions\n.\n\n\ndrop\n.\npost\n(\nform\n)\n \n{\n \nrequest\n \nin\n\n \nreturn\n \nSubmitted with a POST request\n\n\n}\n\n\n\n\n\n\nNesting\n\n\nTo nest paths (adding \n/\ns in the URL), simply add commas.\n\n\ndrop\n.\nget\n(\nfoo\n,\n \nbar\n,\n \nbaz\n)\n \n{\n \nrequest\n \nin\n\n \nreturn\n \nYou requested /foo/bar/baz\n\n\n}\n\n\n\n\n\n\nYou can also use \n/\n, but commas are often easier to type and work better with type safe route \nparameters\n.\n\n\nAlternate\n\n\nAn alternate syntax that accepts a \nMethod\n as the first parameter is also available.\n\n\ndrop\n.\nadd\n(.\ntrace\n,\n \nwelcome\n)\n \n{\n \nrequest\n \nin\n\n \nreturn\n \nHello\n\n\n}\n\n\n\n\n\n\nThis may be useful if you want to register routes dynamically or use a less common method.\n\n\nRequest\n\n\nEach route closure is given a single \nRequest\n. This contains all of the data associated with the request that led to your route closure being called.\n\n\nResponse Representable\n\n\nA route closure can return in three ways:\n\n\n\n\nResponse\n\n\nResponseRepresentable\n\n\nthrow\n\n\n\n\nResponse\n\n\nA custom \nResponse\n can be returned.\n\n\ndrop\n.\nget\n(\nvapor\n)\n \n{\n \nrequest\n \nin\n\n \nreturn\n \nResponse\n(\nredirect\n:\n \nhttp://vapor.codes\n)\n\n\n}\n\n\n\n\n\n\nThis 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.\n\n\nResponse Representable\n\n\nAs you have seen in the previous examples, \nString\ns can be returned in route closures. This is because they conform to \nResponseRepresentable\n\n\nA lot of types in Vapor conform to this protocol by default:\n- String\n- Int\n- \nJSON\n\n- \nModel\n\n\ndrop\n.\nget\n(\njson\n)\n \n{\n \nrequest\n \nin\n\n \nvar\n \njson\n \n=\n \nJSON\n()\n\n \ntry\n \njson\n.\nset\n(\nnumber\n,\n \n123\n)\n\n \ntry\n \njson\n.\nset\n(\ntext\n,\n \nunicorns\n)\n\n \ntry\n \njson\n.\nset\n(\nbool\n,\n \nfalse\n)\n\n \nreturn\n \njson\n\n\n}\n\n\n\n\n\n\nThrowing\n\n\nIf you are unable to return a response, you may \nthrow\n any object that conforms to \nError\n. Vapor comes with a default error enum \nAbort\n.\n\n\ndrop\n.\nget\n(\n404\n)\n \n{\n \nrequest\n \nin\n\n \nthrow\n \nAbort\n(.\nnotFound\n)\n\n\n}\n\n\n\n\n\n\nYou can customize the message of these errors by using \nAbort\n\n\ndrop\n.\nget\n(\nerror\n)\n \n{\n \nrequest\n \nin\n\n \nthrow\n \nAbort\n(.\nbadRequest\n,\n \nreason\n:\n \nSorry \ud83d\ude31\n)\n\n\n}\n\n\n\n\n\n\nThese errors are caught by default in the \nErrorMiddleware\n where they are turned into a JSON response like the following.\n\n\n{\n\n \nerror:\n \ntrue,\n\n \nmessage:\n \nthe message\n\n\n}\n\n\n\n\n\n\nIf you want to override this behavior, remove the \nErrorMiddleware\n (key: \n\"error\"\n) from the \nDroplet\n's middleware and add your own.\n\n\nFallback\n\n\nFallback routes allow you to match multiple layers of nesting slashes.\n\n\napp\n.\nget\n(\nanything\n,\n \n*\n)\n \n{\n \nrequest\n \nin\n\n \nreturn\n \nMatches anything after /anything\n\n\n}\n\n\n\n\n\n\nFor example, the above route matches all of the following and more:\n\n\n\n\n/anything\n\n\n/anything/foo\n\n\n/anything/foo/bar\n\n\n/anything/foo/bar/baz\n\n\n...", - "title": "Overview" - }, - { - "location": "/routing/overview/#basic-routing", - "text": "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.", - "title": "Basic Routing" - }, - { - "location": "/routing/overview/#register", - "text": "The most basic route includes a method, path, and closure. drop . get ( welcome ) { request in \n return Hello } The standard HTTP methods are available including get , post , put , patch , delete , and options . drop . post ( form ) { request in \n return Submitted with a POST request }", - "title": "Register" - }, - { - "location": "/routing/overview/#nesting", - "text": "To nest paths (adding / s in the URL), simply add commas. drop . get ( foo , bar , baz ) { request in \n 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 .", - "title": "Nesting" - }, - { - "location": "/routing/overview/#alternate", - "text": "An alternate syntax that accepts a Method as the first parameter is also available. drop . add (. trace , welcome ) { request in \n return Hello } This may be useful if you want to register routes dynamically or use a less common method.", - "title": "Alternate" - }, - { - "location": "/routing/overview/#request", - "text": "Each route closure is given a single Request . This contains all of the data associated with the request that led to your route closure being called.", - "title": "Request" - }, - { - "location": "/routing/overview/#response-representable", - "text": "A route closure can return in three ways: Response ResponseRepresentable throw", - "title": "Response Representable" - }, - { - "location": "/routing/overview/#response", - "text": "A custom Response can be returned. drop . get ( vapor ) { request in \n 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.", - "title": "Response" - }, - { - "location": "/routing/overview/#response-representable_1", - "text": "As you have seen in the previous examples, String s can be returned in route closures. This is because they conform to ResponseRepresentable A lot of types in Vapor conform to this protocol by default:\n- String\n- Int\n- JSON \n- Model drop . get ( json ) { request in \n var json = JSON () \n try json . set ( number , 123 ) \n try json . set ( text , unicorns ) \n try json . set ( bool , false ) \n return json }", - "title": "Response Representable" - }, - { - "location": "/routing/overview/#throwing", - "text": "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 . drop . get ( 404 ) { request in \n throw Abort (. notFound ) } You can customize the message of these errors by using Abort drop . get ( error ) { request in \n throw Abort (. badRequest , reason : Sorry \ud83d\ude31 ) } These errors are caught by default in the ErrorMiddleware where they are turned into a JSON response like the following. { \n error: true, \n message: the message } If you want to override this behavior, remove the ErrorMiddleware (key: \"error\" ) from the Droplet 's middleware and add your own.", - "title": "Throwing" - }, - { - "location": "/routing/overview/#fallback", - "text": "Fallback routes allow you to match multiple layers of nesting slashes. app . get ( anything , * ) { request in \n 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 ...", - "title": "Fallback" - }, - { - "location": "/routing/parameters/", - "text": "Routing Parameters\n\n\nTraditional 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.\n\n\n\n\nSeealso\n\n\nRoute parameters refer to segments of the URL path (e.g., \n/users/:id\n). For query parameters (e.g., \n?foo=bar\n) see \nrequest query parameters\n.\n\n\n\n\nType Safe\n\n\nTo create a type safe route simply replace one of the parts of your path with a \nType\n.\n\n\ndrop\n.\nget\n(\nusers\n,\n \nInt\n.\nparameter\n)\n \n{\n \nreq\n \nin\n\n \nlet\n \nuserId\n \n=\n \ntry\n \nreq\n.\nparameters\n.\nnext\n(\nInt\n.\nself\n)\n\n \nreturn\n \nYou requested User #\n\\(\nuserId\n)\n\n\n}\n\n\n\n\n\n\nThis creates a route that matches \nusers/:id\n where the \n:id\n is an \nInt\n. Here's what it would look like using manual route parameters.\n\n\ndrop\n.\nget\n(\nusers\n,\n \n:id\n)\n \n{\n \nrequest\n \nin\n\n \nguard\n \nlet\n \nuserId\n \n=\n \nrequest\n.\nparameters\n[\nid\n]?.\nint\n \nelse\n \n{\n\n \nthrow\n \nAbort\n.\nbadRequest\n\n \n}\n\n\n \nreturn\n \nYou requested User #\n\\(\nuserId\n)\n\n\n}\n\n\n\n\n\n\nHere you can see that type safe routing saves ~3 lines of code and also prevents runtime errors like misspelling \n:id\n.\n\n\nParameterizable\n\n\nAny type conforming to \nParameterizable\n can be used as a parameter. By default, all Vapor \nModels\n conform.\n\n\nUsing this, our previous example with users can be further simplified.\n\n\ndrop\n.\nget\n(\nusers\n,\n \nUser\n.\nparameter\n)\n \n{\n \nreq\n \nin\n\n \nlet\n \nuser\n \n=\n \ntry\n \nreq\n.\nparameters\n.\nnext\n(\nUser\n.\nself\n)\n\n\n \nreturn\n \nYou requested \n\\(\nuser\n.\nname\n)\n\n\n}\n\n\n\n\n\n\nHere the identifier supplied is automatically used to lookup a user. For example, if \n/users/5\n is requested, the \nUser\n model will be asked for a user with identifier \n5\n. If one is found, the request succeeds and the closure is called. If not, a not found error is thrown.\n\n\nHere is what this would look like if we looked the model up manually.\n\n\ndrop\n.\nget\n(\nusers\n,\n \nInt\n.\nparameter\n)\n \n{\n \nreq\n \nin\n\n \nlet\n \nuserId\n \n=\n \ntry\n \nreq\n.\nparameters\n.\nnext\n(\nInt\n.\nself\n)\n\n \nguard\n \nlet\n \nuser\n \n=\n \ntry\n \nUser\n.\nfind\n(\nuserId\n)\n \nelse\n \n{\n\n \nthrow\n \nAbort\n.\nnotFound\n\n \n}\n\n\n \nreturn\n \nYou requested \n\\(\nuser\n.\nname\n)\n\n\n}\n\n\n\n\n\n\nProtocol\n\n\nYou can conform your own types to \nParameterizable\n.\n\n\nimport\n \nRouting\n\n\n\nextension\n \nFoo\n:\n \nParameterizable\n \n{\n\n \n/// This unique slug is used to identify\n\n \n/// the parameter in the router\n\n \nstatic\n \nvar\n \nuniqueSlug\n:\n \nString\n \n{\n\n \nreturn\n \nfoo\n\n \n}\n\n\n\n \nstatic\n \nfunc\n \nmake\n(\nfor\n \nparameter\n:\n \nString\n)\n \nthrows\n \n-\n \nFoo\n \n{\n\n \n/// custom lookup logic here\n\n \n/// the parameter string contains the information\n\n \n/// parsed from the URL.\n\n \n...\n\n \n}\n\n\n}\n\n\n\n\n\n\nNow you can use this type for type safe routing.\n\n\ndrop\n.\nget\n(\nusers\n,\n \nnickname\n,\n \nFoo\n.\nparameter\n)\n \n{\n \nreq\n \nin\n\n \nlet\n \nfoo\n \n=\n \ntry\n \nreq\n.\nparameters\n.\nnext\n(\nFoo\n.\nself\n)\n\n \n...\n\n\n}\n\n\n\n\n\n\nGroups\n\n\nType-safe parameters also work with \ngroups\n.\n\n\nlet\n \nuserGroup\n \n=\n \ndrop\n.\ngrouped\n(\nusers\n,\n \nUser\n.\nparameter\n)\n\n\nuserGroup\n.\nget\n(\nmessages\n)\n \n{\n \nreq\n \nin\n\n \nlet\n \nuser\n \n=\n \ntry\n \nreq\n.\nparameters\n.\nnext\n(\nUser\n.\nself\n)\n\n\n \n...\n\n\n}", - "title": "Parameters" - }, - { - "location": "/routing/parameters/#routing-parameters", - "text": "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. Seealso Route parameters refer to segments of the URL path (e.g., /users/:id ). For query parameters (e.g., ?foo=bar ) see request query parameters .", - "title": "Routing Parameters" - }, - { - "location": "/routing/parameters/#type-safe", - "text": "To create a type safe route simply replace one of the parts of your path with a Type . drop . get ( users , Int . parameter ) { req in \n let userId = try req . parameters . next ( Int . self ) \n 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. drop . get ( users , :id ) { request in \n guard let userId = request . parameters [ id ]?. int else { \n throw Abort . badRequest \n } \n\n 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 .", - "title": "Type Safe" - }, - { - "location": "/routing/parameters/#parameterizable", - "text": "Any type conforming to Parameterizable can be used as a parameter. By default, all Vapor Models conform. Using this, our previous example with users can be further simplified. drop . get ( users , User . parameter ) { req in \n let user = try req . parameters . next ( User . self ) \n\n 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 we looked the model up manually. drop . get ( users , Int . parameter ) { req in \n let userId = try req . parameters . next ( Int . self ) \n guard let user = try User . find ( userId ) else { \n throw Abort . notFound \n } \n\n return You requested \\( user . name ) }", - "title": "Parameterizable" - }, - { - "location": "/routing/parameters/#protocol", - "text": "You can conform your own types to Parameterizable . import Routing extension Foo : Parameterizable { \n /// This unique slug is used to identify \n /// the parameter in the router \n static var uniqueSlug : String { \n return foo \n } \n\n\n static func make ( for parameter : String ) throws - Foo { \n /// custom lookup logic here \n /// the parameter string contains the information \n /// parsed from the URL. \n ... \n } } Now you can use this type for type safe routing. drop . get ( users , nickname , Foo . parameter ) { req in \n let foo = try req . parameters . next ( Foo . self ) \n ... }", - "title": "Protocol" - }, - { - "location": "/routing/parameters/#groups", - "text": "Type-safe parameters also work with groups . let userGroup = drop . grouped ( users , User . parameter ) userGroup . get ( messages ) { req in \n let user = try req . parameters . next ( User . self ) \n\n ... }", - "title": "Groups" - }, - { - "location": "/routing/group/", - "text": "Route Groups\n\n\nGrouping routes together makes it easy to add common prefixes, middleware, or hosts to multiple routes.\n\n\nRoute groups have two different forms: Group and Grouped.\n\n\nGroup\n\n\nGroup (without the \"ed\" at the end) takes a closure that is passed a \nRouteBuilder\n.\n\n\ndrop\n.\ngroup\n(\nv1\n)\n \n{\n \nv1\n \nin\n\n \nv1\n.\nget\n(\nusers\n)\n \n{\n \nrequest\n \nin\n\n \n// get the users\n\n \n}\n\n\n}\n\n\n\n\n\n\nGrouped\n\n\nGrouped returns a \nRouteBuilder\n that you can pass around.\n\n\nlet\n \nv1\n \n=\n \ndrop\n.\ngrouped\n(\nv1\n)\n\n\nv1\n.\nget\n(\nusers\n)\n \n{\n \nrequest\n \nin\n\n \n// get the users\n\n\n}\n\n\n\n\n\n\nMiddleware\n\n\nYou can add middleware to a group of routes. This is especially useful for authentication.\n\n\ndrop\n.\ngroup\n(\nAuthMiddleware\n())\n \n{\n \nauthorized\n \nin\n \n \nauthorized\n.\nget\n(\ntoken\n)\n \n{\n \nrequest\n \nin\n\n \n// has been authorized\n\n \n}\n\n\n}\n\n\n\n\n\n\nHost\n\n\nYou can limit the host for a group of routes.\n\n\ndrop\n.\ngroup\n(\nhost\n:\n \nvapor.codes\n)\n \n{\n \nvapor\n \nin\n\n \nvapor\n.\nget\n \n{\n \nrequest\n \nin\n\n \n// only responds to requests to vapor.codes\n\n \n}\n\n\n}\n\n\n\n\n\n\nChaining\n\n\nGroups can be chained together.\n\n\ndrop\n.\ngrouped\n(\nhost\n:\n \nvapor.codes\n).\ngrouped\n(\nAuthMiddleware\n()).\ngroup\n(\nv1\n)\n \n{\n \nauthedSecureV1\n \nin\n\n \n// add routes here\n\n\n}", - "title": "Group" - }, - { - "location": "/routing/group/#route-groups", - "text": "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.", - "title": "Route Groups" - }, - { - "location": "/routing/group/#group", - "text": "Group (without the \"ed\" at the end) takes a closure that is passed a RouteBuilder . drop . group ( v1 ) { v1 in \n v1 . get ( users ) { request in \n // get the users \n } }", - "title": "Group" - }, - { - "location": "/routing/group/#grouped", - "text": "Grouped returns a RouteBuilder that you can pass around. let v1 = drop . grouped ( v1 ) v1 . get ( users ) { request in \n // get the users }", - "title": "Grouped" - }, - { - "location": "/routing/group/#middleware", - "text": "You can add middleware to a group of routes. This is especially useful for authentication. drop . group ( AuthMiddleware ()) { authorized in \n authorized . get ( token ) { request in \n // has been authorized \n } }", - "title": "Middleware" - }, - { - "location": "/routing/group/#host", - "text": "You can limit the host for a group of routes. drop . group ( host : vapor.codes ) { vapor in \n vapor . get { request in \n // only responds to requests to vapor.codes \n } }", - "title": "Host" - }, - { - "location": "/routing/group/#chaining", - "text": "Groups can be chained together. drop . grouped ( host : vapor.codes ). grouped ( AuthMiddleware ()). group ( v1 ) { authedSecureV1 in \n // add routes here }", - "title": "Chaining" - }, - { - "location": "/routing/collection/", - "text": "Route Collections\n\n\nRoute collections allow multiple routes and route groups to be organized in different files or modules.\n\n\nExample\n\n\nHere is an example of a route collection for the \nv1\n portion of an API.\n\n\nimport\n \nVapor\n\n\nimport\n \nHTTP\n\n\nimport\n \nRouting\n\n\n\nclass\n \nV1Collection\n:\n \nRouteCollection\n \n{\n\n \nfunc\n \nbuild\n(\n_\n \nbuilder\n:\n \nRouteBuilder\n)\n \n{\n\n \nlet\n \nv1\n \n=\n \nbuilder\n.\ngrouped\n(\nv1\n)\n\n \nlet\n \nusers\n \n=\n \nv1\n.\ngrouped\n(\nusers\n)\n\n \nlet\n \narticles\n \n=\n \nv1\n.\ngrouped\n(\narticles\n)\n\n\n \nusers\n.\nget\n \n{\n \nrequest\n \nin\n\n \nreturn\n \nRequested all users.\n\n \n}\n\n\n \narticles\n.\nget\n(\nArticle\n.\ninit\n)\n \n{\n \nrequest\n,\n \narticle\n \nin\n\n \nreturn\n \nRequested \n\\(\narticle\n.\nname\n)\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nThis class could be placed in any file, and we could add it to our droplet or even another route group.\n\n\nlet\n \nv1\n \n=\n \nV1Collection\n()\n\n\ndrop\n.\ncollection\n(\nv1\n)\n\n\n\n\n\n\nThe \nDroplet\n will then be passed to the \nbuild(_:)\n method of your route collection and have the various routes added to it.\n\n\nEmpty Initializable\n\n\nYou can add \nEmptyInitializable\n to your route collection if it has an empty \ninit\n method. This will allow you to add the route collection via its type name.\n\n\nclass\n \nV1Collection\n:\n \nRouteCollection\n,\n \nEmptyInitializable\n \n{\n\n \ninit\n()\n \n{\n \n}\n\n \n...\n\n\n\n\n\n\nNow we can add the collection without initializing it.\n\n\ndrop\n.\ncollection\n(\nV1Collection\n.\nself\n)", - "title": "Collection" - }, - { - "location": "/routing/collection/#route-collections", - "text": "Route collections allow multiple routes and route groups to be organized in different files or modules.", - "title": "Route Collections" - }, - { - "location": "/routing/collection/#example", - "text": "Here is an example of a route collection for the v1 portion of an API. import Vapor import HTTP import Routing class V1Collection : RouteCollection { \n func build ( _ builder : RouteBuilder ) { \n let v1 = builder . grouped ( v1 ) \n let users = v1 . grouped ( users ) \n let articles = v1 . grouped ( articles ) \n\n users . get { request in \n return Requested all users. \n } \n\n articles . get ( Article . init ) { request , article in \n return Requested \\( article . name ) \n } \n } } This class could be placed in any file, and we could add it to our droplet or even another route group. 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.", - "title": "Example" - }, - { - "location": "/routing/collection/#empty-initializable", - "text": "You can 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. class V1Collection : RouteCollection , EmptyInitializable { \n init () { } \n ... Now we can add the collection without initializing it. drop . collection ( V1Collection . self )", - "title": "Empty Initializable" - }, - { - "location": "/fluent/package/", - "text": "Using Fluent\n\n\nThis section outlines how to import the Fluent package both with or without a Vapor project.\n\n\nWith Vapor\n\n\nFluent comes included with most Vapor templates. However, if you have created a project from scratch you will need to add the provider to your \nPackage.swift\n file.\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/vapor.git\n,\n \nmajorVersion\n:\n \n2\n),\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/fluent-provider.git\n,\n \nmajorVersion\n:\n \n1\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nThe Fluent provider package adds Fluent to your project and adds some additional, Vapor-specific conveniences like HTTP conformances. \n\n\nUsing \nimport FluentProvider\n will import both Fluent and Fluent's Vapor-specific APIs. \n\n\nWithout Vapor\n\n\nFluent 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 \nPackage.swift\n file.\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n...\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/fluent.git\n,\n \nmajorVersion\n:\n \n2\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Fluent\n to access Fluent's APIs.\n\n\n\n\nWarning\n\n\nModel\n is a Vapor + Fluent type, use \nEntity\n instead.\n\n\n\n\nDrivers\n\n\nFluent 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 \nfluent-driver\n tag on GitHub.", - "title": "Package" - }, - { - "location": "/fluent/package/#using-fluent", - "text": "This section outlines how to import the Fluent package both with or without a Vapor project.", - "title": "Using Fluent" - }, - { - "location": "/fluent/package/#with-vapor", - "text": "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. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n . Package ( url : https://github.com/vapor/vapor.git , majorVersion : 2 ), \n . Package ( url : https://github.com/vapor/fluent-provider.git , majorVersion : 1 ) \n ], \n 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.", - "title": "With Vapor" - }, - { - "location": "/fluent/package/#without-vapor", - "text": "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. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . Package ( url : https://github.com/vapor/fluent.git , majorVersion : 2 ) \n ], \n exclude : [ ... ] ) Use import Fluent to access Fluent's APIs. Warning Model is a Vapor + Fluent type, use Entity instead.", - "title": "Without Vapor" - }, - { - "location": "/fluent/package/#drivers", - "text": "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 tag on GitHub.", - "title": "Drivers" - }, - { - "location": "/fluent/getting-started/", - "text": "Getting Started with Fluent\n\n\nFluent provides an easy, simple, and safe API for working with your persisted data. Each database table/collection is represented by a \nModel\n 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. \n\n\n\n\nNote\n\n\nDon't forget to add \nimport FluentProvider\n (or your other database provider) to the top of your Swift files.\n\n\n\n\nFluent 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 \ndrivers\n.\n\n\nCreating a Model\n\n\nModels are the Swift representations of the data in your database. As such, they are central to most of Fluent's APIs.\n\n\nLet's take a look at what a simple model looks like.\n\n\nfinal\n \nclass\n \nPet\n:\n \nModel\n \n{\n\n \nvar\n \nname\n:\n \nString\n\n \nvar\n \nage\n:\n \nInt\n\n \nlet\n \nstorage\n \n=\n \nStorage\n()\n\n\n \ninit\n(\nrow\n:\n \nRow\n)\n \nthrows\n \n{\n\n \nname\n \n=\n \ntry\n \nrow\n.\nget\n(\nname\n)\n\n \nage\n \n=\n \ntry\n \nrow\n.\nget\n(\nage\n)\n\n \n}\n\n\n \ninit\n(\nname\n:\n \nString\n,\n \nage\n:\n \nInt\n)\n \n{\n\n \nself\n.\nname\n \n=\n \nname\n\n \nself\n.\nage\n \n=\n \nage\n\n \n}\n\n\n \nfunc\n \nmakeRow\n()\n \nthrows\n \n-\n \nRow\n \n{\n\n \nvar\n \nrow\n \n=\n \nRow\n()\n\n \ntry\n \nrow\n.\nset\n(\nname\n,\n \nname\n)\n\n \ntry\n \nrow\n.\nset\n(\nage\n,\n \nage\n)\n\n \nreturn\n \nrow\n\n \n}\n\n\n}\n\n\n\n\n\n\nHere we are creating a simple class \nPet\n with a name and an age. We will add a simple init method for creating new pets.\n\n\nStorage\n\n\nThe \nstorage\n property is there to allow Fluent to store extra information on your model--things like the model's database id. \n\n\nRow\n\n\nThe \nRow\n struct represents a database row. Your models should be able to parse from and serialize to database rows.\n\n\nParse\n\n\nHere's the code for parsing the Pet from the database.\n\n\nfinal\n \nclass\n \nPet\n:\n \nModel\n \n{\n\n \n...\n\n\n \ninit\n(\nrow\n:\n \nRow\n)\n \nthrows\n \n{\n\n \nname\n \n=\n \ntry\n \nrow\n.\nget\n(\nname\n)\n\n \nage\n \n=\n \ntry\n \nrow\n.\nget\n(\nage\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nSerialize\n\n\nHere's the code for serializing the Pet to the database.\n\n\nfinal\n \nclass\n \nPet\n:\n \nModel\n \n{\n\n \n...\n\n\n \nfunc\n \nmakeRow\n()\n \nthrows\n \n-\n \nRow\n \n{\n\n \nvar\n \nrow\n \n=\n \nRow\n()\n\n \ntry\n \nrow\n.\nset\n(\nname\n,\n \nname\n)\n\n \ntry\n \nrow\n.\nset\n(\nage\n,\n \nage\n)\n\n \nreturn\n \nrow\n\n \n}\n\n\n}\n\n\n\n\n\n\nPreparing the Database\n\n\nIn order to use your model, you may need to prepare your database with an appropriate schema.\n\n\nPreparation\n\n\nYou can do this by conforming your model to \nPreparation\n.\n\n\nextension\n \nPet\n:\n \nPreparation\n \n{\n\n \nstatic\n \nfunc\n \nprepare\n(\n_\n \ndatabase\n:\n \nDatabase\n)\n \nthrows\n \n{\n\n \ntry\n \ndatabase\n.\ncreate\n(\nself\n)\n \n{\n \npets\n \nin\n\n \npets\n.\nid\n()\n\n \npets\n.\nstring\n(\nname\n)\n\n \npets\n.\nint\n(\nage\n)\n\n \n}\n\n \n}\n \n\n \nstatic\n \nfunc\n \nrevert\n(\n_\n \ndatabase\n:\n \nDatabase\n)\n \nthrows\n \n{\n\n \ntry\n \ndatabase\n.\ndelete\n(\nself\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nHere we are creating a simple table that will look like this:\n\n\n\n\n\n\n\n\nid\n\n\nname\n\n\nage\n\n\n\n\n\n\n\n\n\n\ndatabase id type\n\n\nstring\n\n\nint\n\n\n\n\n\n\n\n\nAdd to Droplet\n\n\nNow you can add your model to the config's preparations so the database is prepared when your application boots.\n\n\nimport\n \nVapor\n\n\nimport\n \nFluentProvider\n\n\n\nlet\n \nconfig\n \n=\n \ntry\n \nConfig\n()\n\n\nconfig\n.\npreparations\n.\nappend\n(\nPet\n.\nself\n)\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nconfig\n)\n\n\n\n...\n\n\n\n\n\n\nUsing Models\n\n\nNow that we have created our model and prepared the database, we can use it to save and fetch data from the database.\n\n\nSave\n\n\nTo save a model, call \n.save()\n. A new identifier for the model will automatically be created.\n\n\nlet\n \ndog\n \n=\n \nPet\n(\nname\n:\n \nSpud\n,\n \nage\n:\n \n5\n)\n\n\ntry\n \ndog\n.\nsave\n()\n\n\nprint\n(\ndog\n.\nid\n)\n \n// the newly saved pet\ns id\n\n\n\n\n\n\nFind\n\n\nYou can fetch a model from the database using it's ID.\n\n\nguard\n \nlet\n \ndog\n \n=\n \ntry\n \nPet\n.\nfind\n(\n42\n)\n \nelse\n \n{\n\n \nthrow\n \nAbort\n.\nnotFound\n\n\n}\n\n\n\nprint\n(\ndog\n.\nname\n)\n \n// the name of the dog with id 42\n\n\n\n\n\n\nFilter\n\n\nYou can also search for models using filters.\n\n\nlet\n \ndogs\n \n=\n \ntry\n \nPet\n.\nmakeQuery\n().\nfilter\n(\nage\n,\n \n.\ngreaterThan\n,\n \n2\n).\nall\n()\n\n\nprint\n(\ndogs\n)\n \n// all dogs older than 2\n\n\n\n\n\n\nDrivers\n\n\nCheck out the \ndatabase\n section for more information about different database drivers you can use with Fluent.", - "title": "Getting Started" - }, - { - "location": "/fluent/getting-started/#getting-started-with-fluent", - "text": "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 .", - "title": "Getting Started with Fluent" - }, - { - "location": "/fluent/getting-started/#creating-a-model", - "text": "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. final class Pet : Model { \n var name : String \n var age : Int \n let storage = Storage () \n\n init ( row : Row ) throws { \n name = try row . get ( name ) \n age = try row . get ( age ) \n } \n\n init ( name : String , age : Int ) { \n self . name = name \n self . age = age \n } \n\n func makeRow () throws - Row { \n var row = Row () \n try row . set ( name , name ) \n try row . set ( age , age ) \n return row \n } } 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.", - "title": "Creating a Model" - }, - { - "location": "/fluent/getting-started/#storage", - "text": "The storage property is there to allow Fluent to store extra information on your model--things like the model's database id.", - "title": "Storage" - }, - { - "location": "/fluent/getting-started/#row", - "text": "The Row struct represents a database row. Your models should be able to parse from and serialize to database rows.", - "title": "Row" - }, - { - "location": "/fluent/getting-started/#parse", - "text": "Here's the code for parsing the Pet from the database. final class Pet : Model { \n ... \n\n init ( row : Row ) throws { \n name = try row . get ( name ) \n age = try row . get ( age ) \n } }", - "title": "Parse" - }, - { - "location": "/fluent/getting-started/#serialize", - "text": "Here's the code for serializing the Pet to the database. final class Pet : Model { \n ... \n\n func makeRow () throws - Row { \n var row = Row () \n try row . set ( name , name ) \n try row . set ( age , age ) \n return row \n } }", - "title": "Serialize" - }, - { - "location": "/fluent/getting-started/#preparing-the-database", - "text": "In order to use your model, you may need to prepare your database with an appropriate schema.", - "title": "Preparing the Database" - }, - { - "location": "/fluent/getting-started/#preparation", - "text": "You can do this by conforming your model to Preparation . extension Pet : Preparation { \n static func prepare ( _ database : Database ) throws { \n try database . create ( self ) { pets in \n pets . id () \n pets . string ( name ) \n pets . int ( age ) \n } \n } \n\n static func revert ( _ database : Database ) throws { \n try database . delete ( self ) \n } } Here we are creating a simple table that will look like this: id name age database id type string int", - "title": "Preparation" - }, - { - "location": "/fluent/getting-started/#add-to-droplet", - "text": "Now you can add your model to the config's preparations so the database is prepared when your application boots. import Vapor import FluentProvider let config = try Config () config . preparations . append ( Pet . self ) let drop = try Droplet ( config ) ...", - "title": "Add to Droplet" - }, - { - "location": "/fluent/getting-started/#using-models", - "text": "Now that we have created our model and prepared the database, we can use it to save and fetch data from the database.", - "title": "Using Models" - }, - { - "location": "/fluent/getting-started/#save", - "text": "To save a model, call .save() . A new identifier for the model will automatically be created. let dog = Pet ( name : Spud , age : 5 ) try dog . save () print ( dog . id ) // the newly saved pet s id", - "title": "Save" - }, - { - "location": "/fluent/getting-started/#find", - "text": "You can fetch a model from the database using it's ID. guard let dog = try Pet . find ( 42 ) else { \n throw Abort . notFound } print ( dog . name ) // the name of the dog with id 42", - "title": "Find" - }, - { - "location": "/fluent/getting-started/#filter", - "text": "You can also search for models using filters. let dogs = try Pet . makeQuery (). filter ( age , . greaterThan , 2 ). all () print ( dogs ) // all dogs older than 2", - "title": "Filter" - }, - { - "location": "/fluent/getting-started/#drivers", - "text": "Check out the database section for more information about different database drivers you can use with Fluent.", - "title": "Drivers" - }, - { - "location": "/fluent/model/", - "text": "Model\n\n\nModels are the Swift representations of the data in your database. As such, they are central to most of Fluent's APIs. \n\n\nThis guide is an overview of the protocol requirements and methods associated with models.\n\n\n\n\nSeealso\n\n\nCheck out the \ngetting started\n guide for an introductory overview on using models.\n\n\n\n\nCRUD\n\n\nModels have several basic methods for creating, reading, updating, and deleting.\n\n\nSave\n\n\nPersists the entity into the data store and sets the \nid\n property.\n\n\nlet\n \npet\n \n=\n \nPet\n(\nname\n:\n \nSpud\n,\n \nage\n:\n \n2\n)\n\n\ntry\n \npet\n.\nsave\n()\n\n\n\n\n\n\nFind\n\n\nFinds the model with the supplied identifier or returns \nnil\n.\n\n\nguard\n \nlet\n \npet\n \n=\n \ntry\n \nPets\n.\nfind\n(\n42\n)\n \nelse\n \n{\n\n \nthrow\n \nAbort\n.\nnotFound\n\n\n}\n\n\nprint\n(\npet\n.\nname\n)\n\n\n\n\n\n\nDelete\n\n\nDeletes the entity from the data store if the entity has previously been fetched or saved.\n\n\ntry\n \npet\n.\ndelete\n()\n\n\n\n\n\n\nAll\n\n\nReturns all entities for this model.\n\n\nfor\n \npet\n \nin\n \ntry\n \nPets\n.\nall\n()\n \n{\n\n \nprint\n(\npet\n.\nname\n)\n\n\n}\n\n\n\n\n\n\nCount\n\n\nReturns a count of all entities for this model.\n\n\nlet\n \ncount\n \n=\n \ntry\n \nPets\n.\ncount\n()\n\n\n\n\n\n\nChunk\n\n\nReturns chunked arrays of a supplied size for all of the entities for this model.\n\n\nThis is a great way to parse through all models of a large data set.\n\n\ntry\n \nPets\n.\nchunk\n(\n20\n)\n \n{\n \npets\n \nin\n\n \n//\n\n\n}\n\n\n\n\n\n\nQuery\n\n\nCreates a \nQuery\n instance for this \nModel\n.\n\n\nlet\n \nquery\n \n=\n \ntry\n \nPet\n.\nmakeQuery\n()\n\n\n\n\n\n\nTo learn more about crafting complex queries, see the \nquery\n section.\n\n\nTimestamps\n\n\nTo add timestamps to your model, simply conform it to \nTimestampable\n.\n\n\nextension\n \nUser\n:\n \nTimestampable\n \n{\n \n}\n\n\n\n\n\n\nYou can access the updated at and created at times on any model instance.\n\n\nuser\n.\nupdatedAt\n \n// Date?\n\n\nuser\n.\ncreatedAt\n \n// Date?\n\n\n\n\n\n\nWhen filtering or sorting on the timestamp data, you can use the timestamp keys from the class.\n\n\nlet\n \nnewUsers\n \n=\n \ntry\n \nUser\n\n \n.\nmakeQuery\n()\n\n \n.\nfilter\n(\nUser\n.\ncreatedAtKey\n,\n \n.\ngreaterThan\n,\n \n...)\n\n \n.\nall\n()\n\n\n\n\n\n\nYou can also override the timestamp keys if you have custom needs.\n\n\nextension\n \nUser\n:\n \nTimestampable\n \n{\n\n \nstatic\n \nvar\n \nupdatedAtKey\n:\n \nString\n \n{\n \nreturn\n \ncustom_updated_at\n \n}\n\n \nstatic\n \nvar\n \ncreatedAtKey\n:\n \nString\n \n{\n \nreturn\n \ncustom_created_at\n \n}\n\n\n}\n\n\n\n\n\n\nMigration\n\n\nTimestampable\n models will automatically have created at and updated at keys added during\n\ndatabase create\n calls.\n\n\nShould you need to manually add \nTimestampable\n to an existing model, you can use the \ndate()\n method\nin a \nmigration\n.\n\n\ndatabase\n.\nmodify\n(\nUser\n.\nself\n)\n \n{\n \nbuilder\n \nin\n\n \nbuilder\n.\ndate\n(\nUser\n.\ncreatedAtKey\n)\n\n \nbuilder\n.\ndate\n(\nUser\n.\nupdatedAtKey\n)\n\n\n}\n\n\n\n\n\n\nSoft Delete\n\n\nSoft 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. \n\n\nTo make your model soft deletable, simply conform it to \nSoftDeletable\n.\n\n\nextension\n \nUser\n:\n \nSoftDeletable\n \n{\n \n}\n\n\n\n\n\n\nOnce your model is soft deletable, all calls to \ndelete()\n will set the deleted at flag instead of actually\ndeleting the model.\n\n\nTo restore a model, call \n.restore()\n. To actually delete a model from the database, call \n.forceDelete()\n.\n\n\nYou can also override the soft delete key if you have custom needs.\n\n\nextension\n \nUser\n:\n \nSoftDeletable\n \n{\n\n \nstatic\n \nvar\n \ndeletedAtKey\n:\n \nString\n \n{\n \nreturn\n \ncustom_deleted_at\n \n}\n\n\n}\n\n\n\n\n\n\nIncluding Deleted\n\n\nWhen a model is soft deleted, it will not be affected by any queries made with the Fluent query builder.\n\n\nTo include soft deleted models, for instance if you want to restore them, use the \n.withSoftDeleted()\n method\non the query builder.\n\n\nlet\n \nallUsers\n \n=\n \ntry\n \nUser\n.\nmakeQuery\n().\nwithSoftDeleted\n().\nall\n()\n\n\n\n\n\n\nLifecycle\n\n\nYou can hook into the soft delete events of a model.\n\n\nextension\n \nUser\n:\n \nSoftDeletable\n \n{\n\n \nfunc\n \nwillSoftDelete\n()\n \nthrows\n \n{\n \n...\n \n}\n\n \nfunc\n \ndidSoftDelete\n()\n \n{\n \n...\n \n}\n\n \nfunc\n \nwillForceDelete\n()\n \nthrows\n \n{\n \n...\n \n}\n\n \nfunc\n \ndidForceDelete\n()\n \n{\n \n...\n \n}\n\n \nfunc\n \nwillRestore\n()\n \nthrows\n \n{\n \n...\n \n}\n\n \nfunc\n \ndidRestore\n()\n \n{\n \n...\n \n}\n\n\n}\n\n\n\n\n\n\n!!! note:\n Throwing during a \nwill\n hook will prevent the action from happening.\n\n\nMigration\n\n\nSoftDeletable\n models will automatically have a deleted at key added during\n\ndatabase create\n calls.\n\n\nShould you need to manually add \nSoftDeletable\n to an existing model, you can use the \ndate()\n method\nin a \nmigration\n.\n\n\ndatabase\n.\nmodify\n(\nUser\n.\nself\n)\n \n{\n \nbuilder\n \nin\n\n \nbuilder\n.\ndate\n(\nUser\n.\ndeletedAtKey\n,\n \noptional\n:\n \ntrue\n)\n\n\n}\n\n\n\n\n\n\nConvenience\n\n\nAssert Exists\n\n\nThe identifier property of a model is optional since models may not have been saved yet.\n\n\nYou can get the identifier or throw an error if the model has not been saved yet by calling \nassertExists()\n.\n\n\nlet\n \nid\n \n=\n \ntry\n \npet\n.\nassertExists\n()\n\n\nprint\n(\nid\n)\n \n// not optional\n\n\n\n\n\n\nLife Cycle\n\n\nThe following life-cycle methods can be implemented on your model to hook into internal operations.\n\n\n/// Called before the entity will be created.\n\n\n/// Throwing will cancel the creation.\n\n\nfunc\n \nwillCreate\n()\n \nthrows\n\n\n\n/// Called after the entity has been created.\n\n\nfunc\n \ndidCreate\n()\n\n\n\n/// Called before the entity will be updated.\n\n\n/// Throwing will cancel the update.\n\n\nfunc\n \nwillUpdate\n()\n \nthrows\n\n\n\n/// Called after the entity has been updated.\n\n\nfunc\n \ndidUpdate\n()\n\n\n\n/// Called before the entity will be deleted.\n\n\n/// Throwing will cancel the deletion.\n\n\nfunc\n \nwillDelete\n()\n \nthrows\n\n\n\n/// Called after the entity has been deleted.\n\n\nfunc\n \ndidDelete\n()\n\n\n\n\n\n\n\n\nNote\n\n\nThrowing in a \nwillFoo()\n method will cancel the operation.\n\n\n\n\nHere's an example of implementing the \ndidDelete\n method.\n\n\nfinal\n \nclass\n \nPet\n:\n \nModel\n \n{\n\n \n...\n \n\n \nfunc\n \ndidDelete\n()\n \n{\n\n \nprint\n(\nDeleted \n\\(\nname\n)\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nEntity\n\n\nEntity is the base Fluent protocol that Model conforms to. It is responsible for providing all information the \ndatabase or query may need when saving, fetching, or deleting your models.\n\n\nName\n\n\nThe singular relational name of this model. Also used for internal storage. Example: Pet = \"pet\".\n\n\nThis value should usually not be overriden. \n\n\nfinal\n \nclass\n \nPet\n:\n \nModel\n \n{\n\n \nstatic\n \nlet\n \nname\n \n=\n \npet\n\n\n}\n\n\n\n\n\n\nEntity\n\n\nThe plural relational name of this model. Used as the collection or table name. \n\n\nExample: Pet = \"pets\".\n\n\nThis value should be overriden if the table name for your model is non-standard.\n\n\nfinal\n \nclass\n \nPet\n:\n \nModel\n \n{\n\n \nstatic\n \nlet\n \nentity\n \n=\n \npets\n\n\n}\n\n\n\n\n\n\nID Type\n\n\nThe type of identifier used for both the local and foreign id keys. \n\n\nExample: uuid, integer, etc.\n\n\nThis value should be overriden if a particular model in your database uses a different ID type.\n\n\nfinal\n \nclass\n \nPet\n:\n \nModel\n \n{\n\n \nstatic\n \nlet\n \nidType\n:\n \nIdentifierType\n \n=\n \n.\nuuid\n\n\n}\n\n\n\n\n\n\nThis can also be overridden at the database level using config.\n\n\nConfig/fluent.json\n\n\n{\n\n \nidType\n:\n \nuuid\n\n\n}\n\n\n\n\n\n\nOr programatically.\n\n\ndrop\n.\ndatabase\n?.\nidType\n \n=\n \n.\nuuid\n\n\n\n\n\n\nKey Naming Convention\n\n\nThe naming convetion to use for foreign id keys, table names, etc.\n\n\nExample: snake_case vs. camelCase.\n\n\nThis value should be overridden if a particular model in your database uses a different key naming convention.\n\n\nfinal\n \nclass\n \nPet\n:\n \nModel\n \n{\n\n \nstatic\n \nlet\n \nkeyNamingConvention\n \n=\n \n.\nsnake_case\n\n\n}\n\n\n\n\n\n\nThis can also be overridden at the database level using config.\n\n\nConfig/fluent.json\n\n\n{\n\n \nkeyNamingConvention\n:\n \nsnake_case\n\n\n}\n\n\n\n\n\n\nOr programatically.\n\n\ndrop\n.\ndatabase\n?.\nkeyNamingConvention\n \n=\n \n.\nsnake_case\n\n\n\n\n\n\nID Key\n\n\nThe name of the column that corresponds to this entity's identifying key.\n\n\nThe default is 'database.driver.idKey', and then \"id\"\n\n\nfinal\n \nclass\n \nPet\n:\n \nModel\n \n{\n\n \nstatic\n \nlet\n \nidKey\n \n=\n \nid\n\n\n}\n\n\n\n\n\n\nForeign ID Key\n\n\nThe name of the column that points to this entity's id when referenced from other tables or collections.\n\n\nExample: \"foo_id\".\n\n\nfinal\n \nclass\n \nPet\n:\n \nModel\n \n{\n\n \nstatic\n \nlet\n \nforeignIdKey\n \n=\n \npet_id\n\n\n}", - "title": "Model" - }, - { - "location": "/fluent/model/#model", - "text": "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 guide for an introductory overview on using models.", - "title": "Model" - }, - { - "location": "/fluent/model/#crud", - "text": "Models have several basic methods for creating, reading, updating, and deleting.", - "title": "CRUD" - }, - { - "location": "/fluent/model/#save", - "text": "Persists the entity into the data store and sets the id property. let pet = Pet ( name : Spud , age : 2 ) try pet . save ()", - "title": "Save" - }, - { - "location": "/fluent/model/#find", - "text": "Finds the model with the supplied identifier or returns nil . guard let pet = try Pets . find ( 42 ) else { \n throw Abort . notFound } print ( pet . name )", - "title": "Find" - }, - { - "location": "/fluent/model/#delete", - "text": "Deletes the entity from the data store if the entity has previously been fetched or saved. try pet . delete ()", - "title": "Delete" - }, - { - "location": "/fluent/model/#all", - "text": "Returns all entities for this model. for pet in try Pets . all () { \n print ( pet . name ) }", - "title": "All" - }, - { - "location": "/fluent/model/#count", - "text": "Returns a count of all entities for this model. let count = try Pets . count ()", - "title": "Count" - }, - { - "location": "/fluent/model/#chunk", - "text": "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. try Pets . chunk ( 20 ) { pets in \n // }", - "title": "Chunk" - }, - { - "location": "/fluent/model/#query", - "text": "Creates a Query instance for this Model . let query = try Pet . makeQuery () To learn more about crafting complex queries, see the query section.", - "title": "Query" - }, - { - "location": "/fluent/model/#timestamps", - "text": "To add timestamps to your model, simply conform it to Timestampable . extension User : Timestampable { } You can access the updated at and created at times on any model instance. user . updatedAt // Date? user . createdAt // Date? When filtering or sorting on the timestamp data, you can use the timestamp keys from the class. let newUsers = try User \n . makeQuery () \n . filter ( User . createdAtKey , . greaterThan , ...) \n . all () You can also override the timestamp keys if you have custom needs. extension User : Timestampable { \n static var updatedAtKey : String { return custom_updated_at } \n static var createdAtKey : String { return custom_created_at } }", - "title": "Timestamps" - }, - { - "location": "/fluent/model/#migration", - "text": "Timestampable models will automatically have created at and updated at keys added during database create calls. Should you need to manually add Timestampable to an existing model, you can use the date() method\nin a migration . database . modify ( User . self ) { builder in \n builder . date ( User . createdAtKey ) \n builder . date ( User . updatedAtKey ) }", - "title": "Migration" - }, - { - "location": "/fluent/model/#soft-delete", - "text": "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 . extension User : SoftDeletable { } Once your model is soft deletable, all calls to delete() will set the deleted at flag instead of actually\ndeleting 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. extension User : SoftDeletable { \n static var deletedAtKey : String { return custom_deleted_at } }", - "title": "Soft Delete" - }, - { - "location": "/fluent/model/#including-deleted", - "text": "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\non the query builder. let allUsers = try User . makeQuery (). withSoftDeleted (). all ()", - "title": "Including Deleted" - }, - { - "location": "/fluent/model/#lifecycle", - "text": "You can hook into the soft delete events of a model. extension User : SoftDeletable { \n func willSoftDelete () throws { ... } \n func didSoftDelete () { ... } \n func willForceDelete () throws { ... } \n func didForceDelete () { ... } \n func willRestore () throws { ... } \n func didRestore () { ... } } !!! note:\n Throwing during a will hook will prevent the action from happening.", - "title": "Lifecycle" - }, - { - "location": "/fluent/model/#migration_1", - "text": "SoftDeletable models will automatically have a deleted at key added during database create calls. Should you need to manually add SoftDeletable to an existing model, you can use the date() method\nin a migration . database . modify ( User . self ) { builder in \n builder . date ( User . deletedAtKey , optional : true ) }", - "title": "Migration" - }, - { - "location": "/fluent/model/#convenience", - "text": "", - "title": "Convenience" - }, - { - "location": "/fluent/model/#assert-exists", - "text": "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() . let id = try pet . assertExists () print ( id ) // not optional", - "title": "Assert Exists" - }, - { - "location": "/fluent/model/#life-cycle", - "text": "The following life-cycle methods can be implemented on your model to hook into internal operations. /// 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. final class Pet : Model { \n ... \n\n func didDelete () { \n print ( Deleted \\( name ) ) \n } }", - "title": "Life Cycle" - }, - { - "location": "/fluent/model/#entity", - "text": "Entity is the base Fluent protocol that Model conforms to. It is responsible for providing all information the \ndatabase or query may need when saving, fetching, or deleting your models.", - "title": "Entity" - }, - { - "location": "/fluent/model/#name", - "text": "The singular relational name of this model. Also used for internal storage. Example: Pet = \"pet\". This value should usually not be overriden. final class Pet : Model { \n static let name = pet }", - "title": "Name" - }, - { - "location": "/fluent/model/#entity_1", - "text": "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. final class Pet : Model { \n static let entity = pets }", - "title": "Entity" - }, - { - "location": "/fluent/model/#id-type", - "text": "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. final class Pet : Model { \n static let idType : IdentifierType = . uuid } This can also be overridden at the database level using config. Config/fluent.json { \n idType : uuid } Or programatically. drop . database ?. idType = . uuid", - "title": "ID Type" - }, - { - "location": "/fluent/model/#key-naming-convention", - "text": "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. final class Pet : Model { \n static let keyNamingConvention = . snake_case } This can also be overridden at the database level using config. Config/fluent.json { \n keyNamingConvention : snake_case } Or programatically. drop . database ?. keyNamingConvention = . snake_case", - "title": "Key Naming Convention" - }, - { - "location": "/fluent/model/#id-key", - "text": "The name of the column that corresponds to this entity's identifying key. The default is 'database.driver.idKey', and then \"id\" final class Pet : Model { \n static let idKey = id }", - "title": "ID Key" - }, - { - "location": "/fluent/model/#foreign-id-key", - "text": "The name of the column that points to this entity's id when referenced from other tables or collections. Example: \"foo_id\". final class Pet : Model { \n static let foreignIdKey = pet_id }", - "title": "Foreign ID Key" - }, - { - "location": "/fluent/database/", - "text": "Database\n\n\nA Fluent database is responsible for managing connections to your underlying data store and sending queries to the implementation-specific driver you have chosen.\n\n\nDrivers\n\n\nBy default, Fluent includes in-memory and SQLite drivers. There are several drivers available to add to your Vapor application.\n\n\nAvailable\n\n\n\n\n\n\n\n\nType\n\n\nKey\n\n\nPackage\n\n\nClass\n\n\nOfficial\n\n\n\n\n\n\n\n\n\n\nMemory\n\n\nmemory\n\n\nFluent Provider\n\n\nFluent.MemoryDriver\n\n\nYes\n\n\n\n\n\n\nSQlite\n\n\nsqlite\n\n\nFluent Provider\n\n\nFluent.SQLiteDriver\n\n\nYes\n\n\n\n\n\n\nMySQL\n\n\nmysql\n\n\nMySQLProvider\n\n\nMySQLDriver.Driver\n\n\nYes\n\n\n\n\n\n\nPostgreSQL\n\n\npostgresql\n\n\nPostgreSQLProvider\n\n\nPostgreSQLDriver.Driver\n\n\nNo\n\n\n\n\n\n\nMongoDB\n\n\nN/A\n\n\nMongoProvider\n\n\nN/A\n\n\nNo\n\n\n\n\n\n\n\n\nClick on the provider package for more information about how to use it.\n\n\nYou can search for a list of available \nVapor database providers\n on GitHub.\n\n\nDroplet\n\n\nYou can access the database from the Droplet.\n\n\ndrop\n.\ndatabase\n \n// Database?\n\n\n\n\n\n\nPreparations\n\n\nMost databases, like SQL databases, require the schema for a model to be created before it is stored. \nAdding a preparation to your model will allow you to prepare the database while your app boots.\n\n\nextension\n \nUser\n:\n \nPreparation\n \n{\n\n \n/// Prepares a table/collection in the database\n\n \n/// for storing Users\n\n \nstatic\n \nfunc\n \nprepare\n(\n_\n \ndatabase\n:\n \nDatabase\n)\n \nthrows\n \n{\n\n \ntry\n \ndatabase\n.\ncreate\n(\nself\n)\n \n{\n \nbuilder\n \nin\n\n \nbuilder\n.\nid\n()\n\n \nbuilder\n.\nstring\n(\nname\n)\n\n \nbuilder\n.\nint\n(\nage\n)\n\n \n}\n\n \n}\n\n\n \n/// Undoes what was done in `prepare`\n\n \nstatic\n \nfunc\n \nrevert\n(\n_\n \ndatabase\n:\n \nDatabase\n)\n \nthrows\n \n{\n\n \ntry\n \ndatabase\n.\ndelete\n(\nself\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nThe above prepare statement results in SQL similar to the following:\n\n\nCREATE\n \nTABLE\n \n`\nusers\n`\n \n(\n`\nid\n`\n \nINTEGER\n \nPRIMARY\n \nKEY\n \nNOT\n \nNULL\n,\n \n`\nname\n`\n \nTEXT\n \nNOT\n \nNULL\n,\n \n`\nage\n`\n \nINTEGER\n \nNOT\n \nNULL\n)\n\n\n\n\n\n\nOnce you have created you preparation, add it to the Config's prepratations array.\n\n\nconfig\n.\npreparations\n.\nappend\n(\nUser\n.\nself\n)\n\n\n\n\n\n\nCreate\n\n\nThe following methods are available on while creating and modifing the database.\n\n\n\n\n\n\n\n\nMethod\n\n\nType\n\n\n\n\n\n\n\n\n\n\nid\n\n\nPrimary Identifier\n\n\n\n\n\n\nforeignId\n\n\nForeign Identifier\n\n\n\n\n\n\nint\n\n\nInteger\n\n\n\n\n\n\nstring\n\n\nString\n\n\n\n\n\n\ndouble\n\n\nDouble\n\n\n\n\n\n\nbool\n\n\nBoolean\n\n\n\n\n\n\nbytes\n\n\nData\n\n\n\n\n\n\ndate\n\n\nDate + Time\n\n\n\n\n\n\n\n\nYou can use any of these methods on a builder inside \n.create()\n.\n\n\ntry\n \ndatabase\n.\ncreate\n(\nself\n)\n \n{\n \nbuilder\n \nin\n\n \nbuilder\n.\ndouble\n(\nlatitude\n)\n\n \nbuilder\n.\ndouble\n(\nlongitude\n)\n\n\n}\n\n\n\n\n\n\nForeign Keys\n\n\nForeign keys are automatically added with \n.foreignId()\n. To add a foreign key manually, use the\n\n.foreignKey\n method.\n\n\ntry\n \ndatabase\n.\ncreate\n(\nself\n)\n \n{\n \nbuilder\n \nin\n\n \nbuilder\n.\nforeignKey\n(\nuser_id\n,\n \nreferences\n:\n \nid\n,\n \non\n:\n \nUser\n.\nself\n)\n\n\n}\n\n\n\n\n\n\nTo disable automatic foreign keys, set \nautoForeignKeys\n to false in the \nConfig/fluent.json\n file.\n\n\n{\n\n \nautoForeignKeys\n:\n \nfalse\n\n\n}\n\n\n\n\n\n\nModifier\n\n\nExisting schema can be modified using the \n.modify()\n property. All of the methods from \n.create()\n\nare available here as well.\n\n\ntry\n \ndatabase\n.\nmodify\n(\nself\n)\n \n{\n \nbuilder\n \nin\n\n \nbuilder\n.\nstring\n(\nname\n)\n\n \nbuilder\n.\ndelete\n(\nage\n)\n\n\n}\n\n\n\n\n\n\nMigrations\n\n\nOther times, you may want to make some modifications to your data set while migrating to a new version or\njust performing general cleanup. \n\n\nstruct\n \nDeleteOldEntries\n:\n \nPreparation\n \n{\n\n \nstatic\n \nfunc\n \nprepare\n(\n_\n \ndatabase\n:\n \nDatabase\n)\n \nthrows\n \n{\n\n \ntry\n \nLog\n.\nmakeQuery\n().\nfilter\n(...).\ndelete\n()\n\n \n}\n\n\n \n...\n\n\n}\n\n\n\n\n\n\nRun\n\n\nYour preparations will run every time you run your application. You can run your preparations without booting\nyour server by calling:\n\n\nvapor run prepare\n\n\n\n\n\nRevert\n\n\nUse the revert method to undo any work you did in the prepare method. \n\n\nextension\n \nUser\n:\n \nPreparation\n \n{\n\n \n...\n\n\n\n \nstatic\n \nfunc\n \nrevert\n(\n_\n \ndatabase\n:\n \nDatabase\n)\n \nthrows\n \n{\n\n \ntry\n \ndatabase\n.\ndelete\n(\nself\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nYou can run the reversions by calling:\n\n\nvapor run prepare --revert\n\n\n\n\n\nThis will revert the latest batch of preparations. To revert the entire database, run the following:\n\n\nvapor run prepare --revert --all\n\n\n\n\n\nLog\n\n\nLogging queries is a great way to find optimizations for your application and track down bugs.\n\n\nThe easiest way to log queries is to enable logging in your \nfluent.json\n file.\n\n\nConfig/fluent.json\n\n\n{\n\n \n...,\n\n \nlog\n:\n \ntrue\n,\n\n \n...\n\n\n}\n\n\n\n\n\n\nThis will emit info-level logs for all database queries.\n\n\nCustom\n\n\nYou can also hook into the database's query logging callback to execute custom logic.\n\n\ndrop\n.\ndatabase\n?.\nlog\n \n=\n \n{\n \nquery\n \nin\n\n \nprint\n(\nquery\n)\n\n\n}\n\n\n\n\n\n\nYou can assign a closure to the \nlog\n property on the database. Any time a query is run, the closure\nwill be called with a \nQueryLog\n object containing a string describing the statement and the time it ran.\n\n\nTransactions\n\n\nTransactions allow you to group multiple queries into one single unit of work. If any one of the \nqueries experiences a problem, the entire transaction will be rolled back.\n\n\ndrop\n.\ndatabase\n?.\ntransaction\n \n{\n \nconn\n \nin\n\n \ntry\n \nuser\n.\npets\n.\nmakeQuery\n(\nconn\n).\ndelete\n()\n\n \ntry\n \nuser\n.\nmakeQuery\n(\nconn\n).\ndelete\n()\n\n\n}\n\n\n\n\n\n\nDrivers that do not support transactions will throw an error if this method is called.\n\n\nYou can use the \n.makeQuery(_: Executor)\n method to create queries that will run on the \nconnection supplied to the closure.\n\n\n\n\nWarning\n\n\nYou must use the connection supplied to the closure for queries you want to include\nin the transaction.\n\n\n\n\nIndexes\n\n\nAn index is a copy of selected columns of data from a table that can be searched very efficiently.\n\n\nYou can add them to your database by calling \n.index()\n\n\ntry\n \ndatabase\n.\nindex\n(\nname\n,\n \nfor\n:\n \nUser\n.\nself\n)\n\n\n\n\n\n\nYou can delete them by calling \n.deleteIndex()\n\n\ntry\n \ndatabase\n.\ndeleteIndex\n(\nname\n,\n \nfor\n:\n \nUser\n.\nself\n)", - "title": "Database" - }, - { - "location": "/fluent/database/#database", - "text": "A Fluent database is responsible for managing connections to your underlying data store and sending queries to the implementation-specific driver you have chosen.", - "title": "Database" - }, - { - "location": "/fluent/database/#drivers", - "text": "By default, Fluent includes in-memory and SQLite drivers. There are several drivers available to add to your Vapor application.", - "title": "Drivers" - }, - { - "location": "/fluent/database/#available", - "text": "Type Key Package Class Official Memory memory Fluent Provider Fluent.MemoryDriver Yes SQlite sqlite Fluent Provider Fluent.SQLiteDriver Yes MySQL mysql MySQLProvider MySQLDriver.Driver Yes PostgreSQL postgresql PostgreSQLProvider PostgreSQLDriver.Driver No MongoDB N/A MongoProvider 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 on GitHub.", - "title": "Available" - }, - { - "location": "/fluent/database/#droplet", - "text": "You can access the database from the Droplet. drop . database // Database?", - "title": "Droplet" - }, - { - "location": "/fluent/database/#preparations", - "text": "Most databases, like SQL databases, require the schema for a model to be created before it is stored. \nAdding a preparation to your model will allow you to prepare the database while your app boots. extension User : Preparation { \n /// Prepares a table/collection in the database \n /// for storing Users \n static func prepare ( _ database : Database ) throws { \n try database . create ( self ) { builder in \n builder . id () \n builder . string ( name ) \n builder . int ( age ) \n } \n } \n\n /// Undoes what was done in `prepare` \n static func revert ( _ database : Database ) throws { \n try database . delete ( self ) \n } } The above prepare statement results in SQL similar to the following: 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. config . preparations . append ( User . self )", - "title": "Preparations" - }, - { - "location": "/fluent/database/#create", - "text": "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() . try database . create ( self ) { builder in \n builder . double ( latitude ) \n builder . double ( longitude ) }", - "title": "Create" - }, - { - "location": "/fluent/database/#foreign-keys", - "text": "Foreign keys are automatically added with .foreignId() . To add a foreign key manually, use the .foreignKey method. try database . create ( self ) { builder in \n builder . foreignKey ( user_id , references : id , on : User . self ) } To disable automatic foreign keys, set autoForeignKeys to false in the Config/fluent.json file. { \n autoForeignKeys : false }", - "title": "Foreign Keys" - }, - { - "location": "/fluent/database/#modifier", - "text": "Existing schema can be modified using the .modify() property. All of the methods from .create() \nare available here as well. try database . modify ( self ) { builder in \n builder . string ( name ) \n builder . delete ( age ) }", - "title": "Modifier" - }, - { - "location": "/fluent/database/#migrations", - "text": "Other times, you may want to make some modifications to your data set while migrating to a new version or\njust performing general cleanup. struct DeleteOldEntries : Preparation { \n static func prepare ( _ database : Database ) throws { \n try Log . makeQuery (). filter (...). delete () \n } \n\n ... }", - "title": "Migrations" - }, - { - "location": "/fluent/database/#run", - "text": "Your preparations will run every time you run your application. You can run your preparations without booting\nyour server by calling: vapor run prepare", - "title": "Run" - }, - { - "location": "/fluent/database/#revert", - "text": "Use the revert method to undo any work you did in the prepare method. extension User : Preparation { \n ... \n\n\n static func revert ( _ database : Database ) throws { \n try database . delete ( self ) \n } } 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", - "title": "Revert" - }, - { - "location": "/fluent/database/#log", - "text": "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 { \n ..., \n log : true , \n ... } This will emit info-level logs for all database queries.", - "title": "Log" - }, - { - "location": "/fluent/database/#custom", - "text": "You can also hook into the database's query logging callback to execute custom logic. drop . database ?. log = { query in \n print ( query ) } You can assign a closure to the log property on the database. Any time a query is run, the closure\nwill be called with a QueryLog object containing a string describing the statement and the time it ran.", - "title": "Custom" - }, - { - "location": "/fluent/database/#transactions", - "text": "Transactions allow you to group multiple queries into one single unit of work. If any one of the \nqueries experiences a problem, the entire transaction will be rolled back. drop . database ?. transaction { conn in \n try user . pets . makeQuery ( conn ). delete () \n 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 \nconnection supplied to the closure. Warning You must use the connection supplied to the closure for queries you want to include\nin the transaction.", - "title": "Transactions" - }, - { - "location": "/fluent/database/#indexes", - "text": "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() try database . index ( name , for : User . self ) You can delete them by calling .deleteIndex() try database . deleteIndex ( name , for : User . self )", - "title": "Indexes" - }, - { - "location": "/fluent/query/", - "text": "Query\n\n\nFluent's query builder provides a simple interface for creating complex database queries. The \nQuery\n class itself (raw queries excluded) is the sole method by which Fluent communicates with your database.\n\n\nMake\n\n\nYou can create a new query builder from any model class.\n\n\nlet\n \nquery\n \n=\n \ntry\n \nPost\n.\nmakeQuery\n()\n\n\n\n\n\n\nYou can also create queries from an instance. This is especially useful if you need to use a special\ndatabase connection (like for \ntransactions\n) to save or update a model.\n\n\nguard\n \nlet\n \npost\n \n=\n \ntry\n \nPost\n.\nfind\n(\n42\n)\n \nelse\n \n{\n \n...\n \n}\n\n\npost\n.\ncontent\n \n=\n \nUpdated\n\n\nlet\n \nquery\n \n=\n \ntry\n \npost\n.\nmakeQuery\n(\nconn\n).\nsave\n()\n\n\n\n\n\n\nFetch\n\n\nYou have multiple options for fetching the results of a query.\n\n\nAll\n\n\nThe simplest option, \n.all()\n returns all rows relevant to the query.\n\n\nlet users = try User.makeQuery().filter(...).all()\n\n\n\n\n\nFirst\n\n\nYou can take only the first row as well with \n.first()\n.\n\n\nlet user = try User.makeQuery().filter(...).first()\n\n\n\n\n\nFluent will automatically limit the results to \n1\n to increase\nthe performance of the query.\n\n\nChunk\n\n\nIf you want to fetch a large amount of models from the database, using \n.chunk()\n can help reduce the\namount of memory required for the query by fetching chunks of data at a time.\n\n\nUser.makeQuery().filter(...).chunk(32) { users in\n print(users)\n}\n\n\n\n\n\nFilter\n\n\nFilters allow you to choose exactly what subset of data you want to modify or fetch. There are three different\ntypes of filters.\n\n\nCompare\n\n\nCompare filters perform a comparison between a field on your model in the database and a supplied value.\n\n\ntry\n \nquery\n.\nfilter\n(\nage\n,\n \n.\ngreaterThanOrEquals\n,\n \n21\n)\n\n\n\n\n\n\nYou can also use operators.\n\n\ntry\n \nquery\n.\nfilter\n(\nage\n \n=\n \n21\n)\n\n\n\n\n\n\n\n\n\n\n\n\nCase\n\n\nOperator\n\n\nType\n\n\n\n\n\n\n\n\n\n\n.equals\n\n\n==\n\n\nEquals\n\n\n\n\n\n\n.greaterThan\n\n\n\n\nGreater Than\n\n\n\n\n\n\n.lessThan\n\n\n\n\nLess Than\n\n\n\n\n\n\n.greaterThanOrEquals\n\n\n=\n\n\nGreater Than Or Equals\n\n\n\n\n\n\n.lessThanOrEquals\n\n\n=\n\n\nLess Than Or Equals\n\n\n\n\n\n\n.notEquals\n\n\n!=\n\n\nNot Equals\n\n\n\n\n\n\n.hasSuffix\n\n\n\n\nHas Suffix\n\n\n\n\n\n\n.hasPrefix\n\n\n\n\nHas Prefix\n\n\n\n\n\n\n.contains\n\n\n\n\nContains\n\n\n\n\n\n\n.custom(String)\n\n\n\n\nCustom\n\n\n\n\n\n\n\n\n\n\nTip\n\n\nYou can omit the comparison type for \n.equals\n, e.g., \nquery.filter(\"age\", 23)\n\n\n\n\nSubset\n\n\nYou can also filter by fields being in a set of data.\n\n\ntry\n \nquery\n.\nfilter\n(\nfavoriteColor\n,\n \nin\n:\n \n[\npink\n,\n \nblue\n])\n\n\n\n\n\n\nOr the opposite.\n\n\ntry\n \nquery\n.\nfilter\n(\nfavoriteColor\n,\n \nnotIn\n:\n \n[\nbrown\n,\n \nblack\n])\n\n\n\n\n\n\nGroup\n\n\nBy default, all query filters are joined by AND logic. You can create groups of filters within\nyour query that are joined with AND or OR logic.\n\n\ntry\n \nquery\n.\nor\n \n{\n \norGroup\n \nin\n\n \ntry\n \norGroup\n.\nfilter\n(\nage\n,\n \n.\ngreaterThan\n,\n \n75\n)\n\n \ntry\n \norGroup\n.\nfilter\n(\nage\n,\n \n.\nlessThan\n,\n \n18\n)\n\n\n}\n\n\n\n\n\n\nThis will result in SQL similar to the following:\n\n\nSELECT\n \n*\n \nFROM\n \n`\nusers\n`\n \nWHERE\n \n(\n`\nage\n`\n \n \n75\n \nOR\n \n`\nage\n`\n \n \n18\n);\n\n\n\n\n\n\n.and()\n is also available in case you need to switch back to joining filters with AND nested inside of an OR.\n\n\nComplex Example\n\n\nlet\n \nusers\n \n=\n \ntry\n \nUser\n\n \n.\nmakeQuery\n()\n\n \n.\nfilter\n(\nplanetOfOrigin\n,\n \n.\ngreaterThan\n,\n \nEarth\n)\n\n \n.\nor\n \n{\n \norGroup\n \nin\n\n \norGroup\n.\nand\n \n{\n \nandGroup\n \nin\n\n \nandGroup\n.\nfilter\n(\nname\n,\n \nRick\n)\n\n \nandGroup\n.\nfilter\n(\nfavoriteFood\n,\n \nBeer\n)\n\n \n}\n\n \norGroup\n.\nand\n \n{\n \nandGroup\n \nin\n\n \nandGroup\n.\nfilter\n(\nname\n,\n \nMorty\n)\n\n \nandGroup\n.\nfilter\n(\nfavoriteFood\n,\n \nEyeholes\n)\n\n \n}\n\n \n}\n\n \n.\nall\n()\n\n\n\n\n\n\nThis will result in SQL similar to the following:\n\n\nSELECT\n \n*\n \nFROM\n \n`\nusers\n`\n\n \nWHERE\n \n`\nplanetOfOrigin\n`\n \n=\n \nEarth\n \nAND\n \n(\n\n \n(\n`\nname\n`\n \n=\n \nRick\n \nAND\n \n`\nfavoriteFood\n`\n \n=\n \nBeer\n)\n\n \nOR\n \n(\n`\nname\n`\n \n=\n \nMorty\n \nAND\n \n`\nfavoriteFood\n`\n \n=\n \nEyeholes\n)\n\n \n)\n\n\n\n\n\n\n\n\nNote\n\n\nKeep in mind that the AND/OR logic for a group applies only to the filters added \nwithin\n the group. All filters outside of a filter group will be joined by AND.\n\n\n\n\nRaw\n\n\nRaw filters can be used to filter by values that should not be parameterized.\n\n\ntry\n \nquery\n.\nfilter\n(\nraw\n:\n \ndate \n= CURRENT_TIMESTAMP\n)\n\n\n\n\n\n\nNodeConvertible Conformance\n\n\nFilters 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.\n\n\nExample:\n\n\n{\n \n \nentity\n:\nMyApp.User\n,\n\n \nmethod\n:{\n \n \ntype\n:\ngroup\n,\n\n \nrelation\n:\nand\n,\n\n \nfilters\n:[\n \n \n{\n \n \nentity\n:\nMyApp.User\n,\n\n \nmethod\n:{\n \n \ntype\n:\ncompare\n,\n\n \ncomparison\n:\ngreaterThanOrEquals\n,\n\n \nfield\n:\nage\n,\n\n \nvalue\n:\n18\n\n \n}\n\n \n},\n\n \n{\n \n \nentity\n:\nMyApp.User\n,\n\n \nmethod\n:{\n \n \ntype\n:\ncompare\n,\n\n \ncomparison\n:\nequals\n,\n\n \nfield\n:\ngender\n,\n\n \nvalue\n:\nmale\n\n \n}\n\n \n}\n\n \n]\n\n \n}\n\n\n}\n\n\n\n\n\n\n\n\nNote\n\n\nYou must include the module name in the entity field. \"MyModule.MyEntity\"\n\n\n\n\nDistinct\n\n\nTo select only distinct models from the database, add \n.distinct()\n to your query.\n\n\ntry\n \nquery\n.\ndistinct\n()\n\n\n\n\n\n\nLimit / Offset\n\n\nTo limit or offset your query, use the \n.limit()\n method.\n\n\ntry\n \nquery\n.\nlimit\n(\n20\n,\n \noffset\n:\n \n5\n)\n\n\n\n\n\n\nSort\n\n\nTo sort the results of your query, use the \n.sort()\n method.\n\n\ntry\n \nquery\n.\nsort\n(\nage\n,\n \n.\ndescending\n)\n\n\n\n\n\n\nYou can sort on multiple columns at once by chaining your \n.sort()\n calls.\n\n\ntry\n \nquery\n.\nsort\n(\nage\n,\n \n.\ndescending\n).\nsort\n(\nshoe_size\n)\n\n\n\n\n\n\nJoin\n\n\nYou can join two model tables together, which is useful if you want to filter one model by a property of another.\nFor example, let's say you have a table of Employees which belong to Departments. You want to know which\nDepartments contain Employees who have completed ten years of service.\n\n\nFirst you use the \n.join()\n method on a Department query to join it with the Employee table. Next you chain a\n\n.filter()\n on to the query. Bear in mind you need to explicitly pass the 'joined' model to the filter, otherwise\nFluent will try to filter on the 'base' model.\n\n\nlet\n \ndepartments\n \n=\n \ntry\n \nDepartment\n.\nmakeQuery\n()\n\n \n.\njoin\n(\nEmployee\n.\nself\n)\n\n \n.\nfilter\n(\nEmployee\n.\nself\n,\n \nyears_of_service\n \n=\n \n10\n)\n\n\n\n\n\n\nFluent will work out the relationship fields for you, but you can also specify them yourself with the \nbaseKey\n\nand \njoinedKey\n method parameters, where \nbaseKey\n is the identifier field on the 'base' model (the Department)\nand \njoinedKey\n is the foreign key field on the 'joined' model (the Employee) which relates back to the 'base' model.\n\n\n\n\nTip\n\n\n\n\nFluent supports both inner and outer joins; use the invocation \n.join(kind: .outer, MyModel.self)\n\n\nRaw\n\n\nShould you need to perform a query that the query builder does not support, you can use the raw query.\n\n\ntry\n \ndrop\n.\ndatabase\n?.\nraw\n(\nSELECT @@version\n)\n\n\n\n\n\n\nYou can also use the database of a given model.\n\n\nUser\n.\ndatabase\n?.\nraw\n(\nSELECT * FROM `users`\n)\n\n\n\n\n\n\nBesides 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.", - "title": "Query" - }, - { - "location": "/fluent/query/#query", - "text": "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.", - "title": "Query" - }, - { - "location": "/fluent/query/#make", - "text": "You can create a new query builder from any model class. let query = try Post . makeQuery () You can also create queries from an instance. This is especially useful if you need to use a special\ndatabase connection (like for transactions ) to save or update a model. guard let post = try Post . find ( 42 ) else { ... } post . content = Updated let query = try post . makeQuery ( conn ). save ()", - "title": "Make" - }, - { - "location": "/fluent/query/#fetch", - "text": "You have multiple options for fetching the results of a query.", - "title": "Fetch" - }, - { - "location": "/fluent/query/#all", - "text": "The simplest option, .all() returns all rows relevant to the query. let users = try User.makeQuery().filter(...).all()", - "title": "All" - }, - { - "location": "/fluent/query/#first", - "text": "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\nthe performance of the query.", - "title": "First" - }, - { - "location": "/fluent/query/#chunk", - "text": "If you want to fetch a large amount of models from the database, using .chunk() can help reduce the\namount of memory required for the query by fetching chunks of data at a time. User.makeQuery().filter(...).chunk(32) { users in\n print(users)\n}", - "title": "Chunk" - }, - { - "location": "/fluent/query/#filter", - "text": "Filters allow you to choose exactly what subset of data you want to modify or fetch. There are three different\ntypes of filters.", - "title": "Filter" - }, - { - "location": "/fluent/query/#compare", - "text": "Compare filters perform a comparison between a field on your model in the database and a supplied value. try query . filter ( age , . greaterThanOrEquals , 21 ) You can also use operators. 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)", - "title": "Compare" - }, - { - "location": "/fluent/query/#subset", - "text": "You can also filter by fields being in a set of data. try query . filter ( favoriteColor , in : [ pink , blue ]) Or the opposite. try query . filter ( favoriteColor , notIn : [ brown , black ])", - "title": "Subset" - }, - { - "location": "/fluent/query/#group", - "text": "By default, all query filters are joined by AND logic. You can create groups of filters within\nyour query that are joined with AND or OR logic. try query . or { orGroup in \n try orGroup . filter ( age , . greaterThan , 75 ) \n try orGroup . filter ( age , . lessThan , 18 ) } This will result in SQL similar to the following: 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.", - "title": "Group" - }, - { - "location": "/fluent/query/#complex-example", - "text": "let users = try User \n . makeQuery () \n . filter ( planetOfOrigin , . greaterThan , Earth ) \n . or { orGroup in \n orGroup . and { andGroup in \n andGroup . filter ( name , Rick ) \n andGroup . filter ( favoriteFood , Beer ) \n } \n orGroup . and { andGroup in \n andGroup . filter ( name , Morty ) \n andGroup . filter ( favoriteFood , Eyeholes ) \n } \n } \n . all () This will result in SQL similar to the following: SELECT * FROM ` users ` \n WHERE ` planetOfOrigin ` = Earth AND ( \n ( ` name ` = Rick AND ` favoriteFood ` = Beer ) \n OR ( ` name ` = Morty AND ` favoriteFood ` = Eyeholes ) \n ) 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.", - "title": "Complex Example" - }, - { - "location": "/fluent/query/#raw", - "text": "Raw filters can be used to filter by values that should not be parameterized. try query . filter ( raw : date = CURRENT_TIMESTAMP )", - "title": "Raw" - }, - { - "location": "/fluent/query/#nodeconvertible-conformance", - "text": "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: { \n entity : MyApp.User , \n method :{ \n type : group , \n relation : and , \n filters :[ \n { \n entity : MyApp.User , \n method :{ \n type : compare , \n comparison : greaterThanOrEquals , \n field : age , \n value : 18 \n } \n }, \n { \n entity : MyApp.User , \n method :{ \n type : compare , \n comparison : equals , \n field : gender , \n value : male \n } \n } \n ] \n } } Note You must include the module name in the entity field. \"MyModule.MyEntity\"", - "title": "NodeConvertible Conformance" - }, - { - "location": "/fluent/query/#distinct", - "text": "To select only distinct models from the database, add .distinct() to your query. try query . distinct ()", - "title": "Distinct" - }, - { - "location": "/fluent/query/#limit-offset", - "text": "To limit or offset your query, use the .limit() method. try query . limit ( 20 , offset : 5 )", - "title": "Limit / Offset" - }, - { - "location": "/fluent/query/#sort", - "text": "To sort the results of your query, use the .sort() method. try query . sort ( age , . descending ) You can sort on multiple columns at once by chaining your .sort() calls. try query . sort ( age , . descending ). sort ( shoe_size )", - "title": "Sort" - }, - { - "location": "/fluent/query/#join", - "text": "You can join two model tables together, which is useful if you want to filter one model by a property of another.\nFor example, let's say you have a table of Employees which belong to Departments. You want to know which\nDepartments 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\nFluent will try to filter on the 'base' model. let departments = try Department . makeQuery () \n . join ( Employee . self ) \n . 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 \nand joinedKey method parameters, where baseKey is the identifier field on the 'base' model (the Department)\nand 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)", - "title": "Join" - }, - { - "location": "/fluent/query/#raw_1", - "text": "Should you need to perform a query that the query builder does not support, you can use the raw query. try drop . database ?. raw ( SELECT @@version ) You can also use the database of a given model. 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.", - "title": "Raw" - }, - { - "location": "/fluent/relations/", - "text": "Relations\n\n\nFluent relations allow you to relate your models in three different ways:\n\n\n\n\n\n\n\n\nType\n\n\nRelations\n\n\n\n\n\n\n\n\n\n\nOne to One\n\n\nParent / Child\n\n\n\n\n\n\nOne to Many\n\n\nParent / Children\n\n\n\n\n\n\nMany to Many\n\n\nSiblings\n\n\n\n\n\n\n\n\nOne to Many\n\n\nWe'll start with one-to-many since it's the easiest type of relation to understand.\n\n\nTake the following database schema:\n\n\nusers\n\n\n\n\n\n\n\n\nid\n\n\nname\n\n\n\n\n\n\n\n\n\n\nid type\n\n\nstring\n\n\n\n\n\n\n\n\npets\n\n\n\n\n\n\n\n\nid\n\n\nname\n\n\nuser_id\n\n\n\n\n\n\n\n\n\n\nid type\n\n\nstring\n\n\nid type\n\n\n\n\n\n\n\n\n\n\nSeealso\n\n\nVisit the \ndatabase preparations\n guide for more information\non how to create schema.\n\n\n\n\nHere each pet has exactly one owner (a user) and each owner can have multiple pets. \nThis is a one-to-many relationship. One owner has many pets.\n\n\n\n\nTip\n\n\nUse the \nbuilder.foreignId()\n to create foreign ids like \nuser_id\n. This will automatically\ncreate foreign key constraints and follow pre-set key naming conventions.\n\n\n\n\nChildren\n\n\nTo access the user's pets, we will use the \nChildren\n relation.\n\n\nextension\n \nUser\n \n{\n\n \nvar\n \npets\n:\n \nChildren\nUser\n,\n \nPet\n \n{\n\n \nreturn\n \nchildren\n()\n\n \n}\n\n\n}\n\n\n\n\n\n\nImagine the children relation as \nChildren\nParent, Child\n or \nChildren\nFrom, To\n.\nHere we are relating \nfrom\n the user type \nto\n the pet type.\n\n\nWe can now use this relation to get all of the user's pets.\n\n\nlet\n \npets\n \n=\n \ntry\n \nuser\n.\npets\n.\nall\n()\n \n// [Pet]\n\n\n\n\n\n\nThis will create SQL similar to:\n\n\nSELECT\n \n*\n \nFROM\n \n`\npets\n`\n \nWHERE\n \n`\nuser_id\n`\n \n=\n \n...\n;\n\n\n\n\n\n\nRelations work similarly to \nqueries\n.\n\n\nlet\n \npet\n \n=\n \ntry\n \nuser\n.\npets\n.\nfilter\n(\nname\n,\n \nSpud\n).\nfirst\n()\n\n\n\n\n\n\nParent\n\n\nTo access a pet's owner from the pet, we will use the \nParent\n relation.\n\n\nextension\n \nPet\n \n{\n\n \nlet\n \nuserId\n:\n \nIdentifier\n\n\n \n...\n\n\n \nvar\n \nowner\n:\n \nParent\nPet\n,\n \nUser\n \n{\n\n \nreturn\n \nparent\n(\nid\n:\n \nuserId\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nImagine the parent relation as \nParent\nChild, Parent\n or \nParent\nFrom, To\n.\nHere we are relating \nfrom\n the pet type \nto\n the parent type.\n\n\n\n\nNote\n\n\nNotice the \nParent\n relation requires an identifier to be passed in. \nMake sure to load this identifier in your model's \ninit(row:)\n method.\n\n\n\n\nWe can now use this relation to get the pet's owner.\n\n\nlet\n \nowner\n \n=\n \ntry\n \npet\n.\nowner\n.\nget\n()\n \n// User?\n\n\n\n\n\n\nMigration\n\n\nAdding a parent identifier to the child table can be done using the \n.parent()\n method on\nthe schema builder.\n\n\ntry\n \ndatabase\n.\ncreate\n(\nPet\n.\nself\n)\n \n{\n \nbuilder\n \nin\n\n \n...\n\n \nbuilder\n.\nparent\n(\nUser\n.\nself\n)\n\n\n}\n\n\n\n\n\n\nOne to One\n\n\nOne-to-one relations work exactly the same as one-to-many relations. You can use the\ncode from the previous example and simply call \n.first()\n and all calls from the parent type.\n\n\nHowever, you can add a convenience for doing this. Let's assume we wanted to change the previous\nexample from one-to-many to one-to-one.\n\n\nextension\n \nUser\n \n{\n\n \nfunc\n \npet\n()\n \nthrows\n \n-\n \nPet\n?\n \n{\n\n \nreturn\n \ntry\n \nchildren\n().\nfirst\n()\n\n \n}\n \n\n}\n\n\n\n\n\n\nMany to Many\n\n\nMany to many relations require a table in between to store which model is related to which. \nThis table is called a pivot table.\n\n\nYou can use any entity you want as a pivot, but Fluent provides a default one called \nPivot\n.\n\n\nTake the following schema.\n\n\npets\n\n\n\n\n\n\n\n\nid\n\n\nname\n\n\n\n\n\n\n\n\n\n\nid type\n\n\nstring\n\n\n\n\n\n\n\n\npet_toy\n\n\n\n\n\n\n\n\nid\n\n\npet_id\n\n\ntoy_id\n\n\n\n\n\n\n\n\n\n\nid type\n\n\nid type\n\n\nid type\n\n\n\n\n\n\n\n\ntoys\n\n\n\n\n\n\n\n\nid\n\n\nname\n\n\n\n\n\n\n\n\n\n\nid type\n\n\nstring\n\n\n\n\n\n\n\n\nHere each pet can own many toys and each toy can belong to many pets. This is a many-to-many relationship.\n\n\nSiblings\n\n\nTo represent this many-to-many relationship, we will use the \nSiblings\n relation.\n\n\nextension\n \nPet\n \n{\n\n \nvar\n \ntoys\n:\n \nSiblings\nPet\n,\n \nToy\n,\n \nPivot\nPet\n,\n \nToy\n \n{\n\n \nreturn\n \nsiblings\n()\n\n \n}\n\n\n}\n\n\n\n\n\n\nImagine the siblings relations as \nSiblings\nFrom, To, Through\n.\nHere we are relating \nfrom\n the pet type \nto\n the toy type \nthrough\n the pet/toy pivot.\n\n\n\n\nNote\n\n\nThe generic syntax might look a little intimidating at first, but it allows for a very powerful API.\n\n\n\n\nWith this relation added on pets, we can fetch a pet's toys.\n\n\nlet\n \ntoys\n \n=\n \npet\n.\ntoys\n.\nall\n()\n \n// [Toy]\n\n\n\n\n\n\nThe siblings relation works similarly to \nqueries\n and parent/children relations. \n\n\nMigration\n\n\nIf you are using a \nPivot\n type, you can simply add it to your Droplet's preparation array.\n\n\ndrop\n.\npreparations\n.\nappend\n(\nPivot\nPet\n,\n \nToy\n.\nself\n)\n\n\n\n\n\n\nIf you are using a \nPivot\n for your \"through\" model, it will also have methods for adding and removing models from the relation.\n\n\nAdd\n\n\nTo add a new model to the relation, use the \n.add()\n method.\n\n\ntry\n \npet\n.\ntoys\n.\nadd\n(\ntoy\n)\n\n\n\n\n\n\n\n\nNote\n\n\nThe newly created pivot will be returned.\n\n\n\n\nRemove\n\n\nTo remove a model from being related, use the \n.remove()\n method.\n\n\ntry\n \npet\n.\ntoys\n.\nremove\n(\ntoy\n)\n\n\n\n\n\n\nIs Attached\n\n\nTo check if a model is related, use the \n.isAttached()\n method.\n\n\nif\n \ntry\n \npet\n.\ntoys\n.\nisAttached\n(\nto\n:\n \ntoy\n)\n \n{\n\n \n// it is attached\n\n\n}\n\n\n\n\n\n\nCustom Through\n\n\nYou can use any entity type as the \"through\" entity in your siblings relation. \n\n\nextension\n \nUser\n \n{\n\n \nvar\n \nposts\n:\n \nSiblings\nUser\n,\n \nPost\n,\n \nComment\n \n{\n\n \nreturn\n \nsiblings\n()\n\n \n}\n\n\n}\n\n\n\n\n\n\nIn the above example we are pivoting on the comments entity to retreive all posts the user\nhas commented on.\n\n\nAs long as the \"through\" entity has a \nuser_id\n and \npost_id\n, the siblings relation will work.\n\n\n\n\nNote\n\n\nIf the \nComment\nentity does not conform to \nPivotProtocol\n, the \n\nadd\n, \nremove\n, and \nisAttached\n methods will not be available.", - "title": "Relations" - }, - { - "location": "/fluent/relations/#relations", - "text": "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", - "title": "Relations" - }, - { - "location": "/fluent/relations/#one-to-many", - "text": "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 id type string pets id name user_id id type string id type Seealso Visit the database preparations guide for more information\non how to create schema. Here each pet has exactly one owner (a user) and each owner can have multiple pets. \nThis 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\ncreate foreign key constraints and follow pre-set key naming conventions.", - "title": "One to Many" - }, - { - "location": "/fluent/relations/#children", - "text": "To access the user's pets, we will use the Children relation. extension User { \n var pets : Children User , Pet { \n return children () \n } } Imagine the children relation as Children Parent, Child or Children From, To .\nHere 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. let pets = try user . pets . all () // [Pet] This will create SQL similar to: SELECT * FROM ` pets ` WHERE ` user_id ` = ... ; Relations work similarly to queries . let pet = try user . pets . filter ( name , Spud ). first ()", - "title": "Children" - }, - { - "location": "/fluent/relations/#parent", - "text": "To access a pet's owner from the pet, we will use the Parent relation. extension Pet { \n let userId : Identifier \n\n ... \n\n var owner : Parent Pet , User { \n return parent ( id : userId ) \n } } Imagine the parent relation as Parent Child, Parent or Parent From, To .\nHere we are relating from the pet type to the parent type. Note Notice the Parent relation requires an identifier to be passed in. \nMake sure to load this identifier in your model's init(row:) method. We can now use this relation to get the pet's owner. let owner = try pet . owner . get () // User?", - "title": "Parent" - }, - { - "location": "/fluent/relations/#migration", - "text": "Adding a parent identifier to the child table can be done using the .parent() method on\nthe schema builder. try database . create ( Pet . self ) { builder in \n ... \n builder . parent ( User . self ) }", - "title": "Migration" - }, - { - "location": "/fluent/relations/#one-to-one", - "text": "One-to-one relations work exactly the same as one-to-many relations. You can use the\ncode 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\nexample from one-to-many to one-to-one. extension User { \n func pet () throws - Pet ? { \n return try children (). first () \n } }", - "title": "One to One" - }, - { - "location": "/fluent/relations/#many-to-many", - "text": "Many to many relations require a table in between to store which model is related to which. \nThis 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 id type string pet_toy id pet_id toy_id id type id type id type toys id name id type string Here each pet can own many toys and each toy can belong to many pets. This is a many-to-many relationship.", - "title": "Many to Many" - }, - { - "location": "/fluent/relations/#siblings", - "text": "To represent this many-to-many relationship, we will use the Siblings relation. extension Pet { \n var toys : Siblings Pet , Toy , Pivot Pet , Toy { \n return siblings () \n } } Imagine the siblings relations as Siblings From, To, Through .\nHere 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. let toys = pet . toys . all () // [Toy] The siblings relation works similarly to queries and parent/children relations.", - "title": "Siblings" - }, - { - "location": "/fluent/relations/#migration_1", - "text": "If you are using a Pivot type, you can simply add it to your Droplet's preparation array. 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.", - "title": "Migration" - }, - { - "location": "/fluent/relations/#add", - "text": "To add a new model to the relation, use the .add() method. try pet . toys . add ( toy ) Note The newly created pivot will be returned.", - "title": "Add" - }, - { - "location": "/fluent/relations/#remove", - "text": "To remove a model from being related, use the .remove() method. try pet . toys . remove ( toy )", - "title": "Remove" - }, - { - "location": "/fluent/relations/#is-attached", - "text": "To check if a model is related, use the .isAttached() method. if try pet . toys . isAttached ( to : toy ) { \n // it is attached }", - "title": "Is Attached" - }, - { - "location": "/fluent/relations/#custom-through", - "text": "You can use any entity type as the \"through\" entity in your siblings relation. extension User { \n var posts : Siblings User , Post , Comment { \n return siblings () \n } } In the above example we are pivoting on the comments entity to retreive all posts the user\nhas 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.", - "title": "Custom Through" - }, - { - "location": "/cache/package/", - "text": "Using Cache\n\n\nThis package is included with the Vapor dependency, use\n\n\nimport\n \nCache", - "title": "Package" - }, - { - "location": "/cache/package/#using-cache", - "text": "This package is included with the Vapor dependency, use import Cache", - "title": "Using Cache" - }, - { - "location": "/cache/overview/", - "text": "Cache\n\n\nVapor's \nCacheProtocol\n allows you to store and fetch items from a cache using optional expiration dates.\n\n\nBy default, the Droplet's cache is set to \nMemoryCache\n. See the various \nproviders\n below.\n\n\nStore\n\n\nStoring data into the cache is straightforward.\n\n\ntry\n \ndrop\n.\ncache\n.\nset\n(\nhello\n,\n \nworld\n)\n\n\n\n\n\n\nExpiration\n\n\nWhen storing data, you can also supply an expiration date.\n\n\ntry\n \ndrop\n.\ncache\n.\nset\n(\nephemeral\n,\n \n42\n,\n \nexpiration\n:\n \nDate\n(\ntimeIntervalSinceNow\n:\n \n30\n))\n\n\n\n\n\n\nIn the above example, the supplied key value pair will expire after 30 seconds.\n\n\nFetch\n\n\nYou can retreive data from the cache using the \n.get()\n method.\n\n\ntry\n \ndrop\n.\ncache\n.\nget\n(\nhello\n)\n \n// \nworld\n\n\n\n\n\n\nDelete\n\n\nKeys can be deleted from the cache using the \n.delete()\n method.\n\n\ntry\n \ndrop\n.\ncache\n.\ndelete\n(\nhello\n)\n\n\n\n\n\n\nProviders\n\n\nHere is a list of official cache providers. You can \nsearch GitHub\n for additional packages.\n\n\n\n\n\n\n\n\nType\n\n\nKey\n\n\nDescription\n\n\nPackage\n\n\nClass\n\n\n\n\n\n\n\n\n\n\nMemory\n\n\nmemory\n\n\nIn-memory cache. Not persisted.\n\n\nVapor\n\n\nMemoryCache\n\n\n\n\n\n\nFluent\n\n\nfluent\n\n\nUses Fluent database.\n\n\nFluent Provider\n\n\nFluentCache\n\n\n\n\n\n\nRedis\n\n\nredis\n\n\nUses Redis database.\n\n\nRedisProvider\n\n\nRedisCache\n\n\n\n\n\n\n\n\nHow to Use\n\n\nTo use a different cache provider besides the default \nMemoryCache\n, make sure you have added the provider to your Package.\n\n\nimport\n \nVapor\n\n\nimport\n \npackage\nProvider\n\n\n\nlet\n \nconfig\n \n=\n \ntry\n \nConfig\n()\n\n\ntry\n \nconfig\n.\naddProvider\n(\npackage\nProvider\n.\nProvider\n.\nself\n)\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nconfig\n)\n\n\n\n\n...\n\n\n\n\n\n\nThen change the Droplet's configuration file.\n\n\nConfig/droplet.json\n\n\n{\n\n \ncache\n:\n \nkey\n\n\n}", - "title": "Overview" - }, - { - "location": "/cache/overview/#cache", - "text": "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 below.", - "title": "Cache" - }, - { - "location": "/cache/overview/#store", - "text": "Storing data into the cache is straightforward. try drop . cache . set ( hello , world )", - "title": "Store" - }, - { - "location": "/cache/overview/#expiration", - "text": "When storing data, you can also supply an expiration date. try drop . cache . set ( ephemeral , 42 , expiration : Date ( timeIntervalSinceNow : 30 )) In the above example, the supplied key value pair will expire after 30 seconds.", - "title": "Expiration" - }, - { - "location": "/cache/overview/#fetch", - "text": "You can retreive data from the cache using the .get() method. try drop . cache . get ( hello ) // world", - "title": "Fetch" - }, - { - "location": "/cache/overview/#delete", - "text": "Keys can be deleted from the cache using the .delete() method. try drop . cache . delete ( hello )", - "title": "Delete" - }, - { - "location": "/cache/overview/#providers", - "text": "Here is a list of official cache providers. You can search GitHub for additional packages. Type Key Description Package Class Memory memory In-memory cache. Not persisted. Vapor MemoryCache Fluent fluent Uses Fluent database. Fluent Provider FluentCache Redis redis Uses Redis database. RedisProvider RedisCache", - "title": "Providers" - }, - { - "location": "/cache/overview/#how-to-use", - "text": "To use a different cache provider besides the default MemoryCache , make sure you have added the provider to your Package. 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 { \n cache : key }", - "title": "How to Use" - }, - { - "location": "/mysql/package/", - "text": "Using MySQL\n\n\nThis section outlines how to import the MySQL package both with or without a Vapor project.\n\n\nInstall MySQL\n\n\nTo use MySQL, you need to have the C MySQL library installed on your computer.\n\n\n# macOS\n\n\nbrew install vapor/tap/cmysql\n\n\n\n\n\n\n\nNote for Ubuntu\n* \nAdd Vapor's APT repo to get access to all of Vapor's system packages.\n\n\n\n\n# Ubuntu\n\n\nsudo apt-get install cmysql\n\n\n\n\n\nWith Vapor + Fluent\n\n\nThe easiest way to use MySQL with Vapor is to include the MySQL provider.\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/vapor.git\n,\n \nmajorVersion\n:\n \n2\n),\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/mysql-provider.git\n,\n \nmajorVersion\n:\n \n2\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nThe MySQL provider package adds MySQL to your project and adds some additional, Vapor-specific conveniences like \ndrop.mysql()\n.\n\n\nUsing \nimport MySQLProvider\n will import both Fluent and Fluent's Vapor-specific APIs.\n\n\nWith Fluent\n\n\nFluent is a powerful, pure-Swift ORM that can be used with any Server-Side Swift framework. The MySQL driver allows you to use a MySQL database to power your models and queries.\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n...\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/fluent.git\n,\n \nmajorVersion\n:\n \n2\n),\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/mysql-driver.git\n,\n \nmajorVersion\n:\n \n2\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport MySQLDriver\n to access the \nMySQLDriver\n class which you can use to initialize a Fluent \nDatabase\n.\n\n\nJust MySQL\n\n\nAt the core of the MySQL provider and MySQL driver is a Swift wrapper around the C MySQL client. This package can be used by itself to send raw, parameterized queries to your MySQL database.\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n...\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/mysql.git\n,\n \nmajorVersion\n:\n \n2\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport MySQL\n to access the \nMySQL.Database\n class.", - "title": "Package" - }, - { - "location": "/mysql/package/#using-mysql", - "text": "This section outlines how to import the MySQL package both with or without a Vapor project.", - "title": "Using MySQL" - }, - { - "location": "/mysql/package/#install-mysql", - "text": "To use MySQL, you need to have the C MySQL library installed on your computer. # macOS \n\nbrew install vapor/tap/cmysql Note for Ubuntu\n* Add Vapor's APT repo to get access to all of Vapor's system packages. # Ubuntu \n\nsudo apt-get install cmysql", - "title": "Install MySQL" - }, - { - "location": "/mysql/package/#with-vapor-fluent", - "text": "The easiest way to use MySQL with Vapor is to include the MySQL provider. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n . Package ( url : https://github.com/vapor/vapor.git , majorVersion : 2 ), \n . Package ( url : https://github.com/vapor/mysql-provider.git , majorVersion : 2 ) \n ], \n exclude : [ ... ] ) The MySQL provider package adds MySQL to your project and adds some additional, Vapor-specific conveniences like drop.mysql() . Using import MySQLProvider will import both Fluent and Fluent's Vapor-specific APIs.", - "title": "With Vapor + Fluent" - }, - { - "location": "/mysql/package/#with-fluent", - "text": "Fluent is a powerful, pure-Swift ORM that can be used with any Server-Side Swift framework. The MySQL driver allows you to use a MySQL database to power your models and queries. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . Package ( url : https://github.com/vapor/fluent.git , majorVersion : 2 ), \n . Package ( url : https://github.com/vapor/mysql-driver.git , majorVersion : 2 ) \n ], \n exclude : [ ... ] ) Use import MySQLDriver to access the MySQLDriver class which you can use to initialize a Fluent Database .", - "title": "With Fluent" - }, - { - "location": "/mysql/package/#just-mysql", - "text": "At the core of the MySQL provider and MySQL driver is a Swift wrapper around the C MySQL client. This package can be used by itself to send raw, parameterized queries to your MySQL database. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . Package ( url : https://github.com/vapor/mysql.git , majorVersion : 2 ) \n ], \n exclude : [ ... ] ) Use import MySQL to access the MySQL.Database class.", - "title": "Just MySQL" - }, - { - "location": "/mysql/provider/", - "text": "MySQL Provider\n\n\nAfter you've \nadded the MySQL Provider package\n to your project, setting the provider up in code is easy.\n\n\nAdd to Droplet\n\n\nFirst, register the \nMySQLProvider.Provider\n with your Droplet.\n\n\nimport\n \nVapor\n\n\nimport\n \nMySQLProvider\n\n\n\nlet\n \nconfig\n \n=\n \ntry\n \nConfig\n()\n\n\ntry\n \nconfig\n.\naddProvider\n(\nMySQLProvider\n.\nProvider\n.\nself\n)\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nconfig\n)\n\n\n\n...\n\n\n\n\n\n\nConfigure Fluent\n\n\nOnce the provider is added to your Droplet, you can configure Fluent to use the MySQL driver.\n\n\nConfig/fluent.json\n\n\n{\n\n \ndriver\n:\n \nmysql\n\n\n}\n\n\n\n\n\n\n\n\nSeealso\n\n\nLearn more about configuration files in the \nSettings guide\n.\n\n\n\n\nConfigure MySQL\n\n\nIf you run your application now, you will likely see an error that the MySQL configuration file is missing. Let's add that now.\n\n\nBasic\n\n\nHere is an example of a simple MySQL configuration file.\n\n\nConfig/mysql.json\n\n\n{\n\n \nhostname\n:\n \n127.0.0.1\n,\n\n \nuser\n:\n \nroot\n,\n\n \npassword\n:\n \npassword\n,\n\n \ndatabase\n:\n \nhello\n\n\n}\n\n\n\n\n\n\n\n\nNote\n\n\nIt's a good idea to store the MySQL configuration file in the \nConfig/secrets\n folder since it contains sensitive information.\n\n\n\n\nURL\n\n\nYou can also pass the MySQL credentials as a URL.\n\n\nConfig/mysql.json\n\n\n{\n\n \nurl\n:\n \nhttp://root:password@127.0.0.1/hello\n\n\n}\n\n\n\n\n\n\nRead Replicas\n\n\nRead replicas can be supplied by passing a single \nmaster\n hostname and an array of \nreadReplicas\n hostnames.\n\n\nConfig/mysql.json\n\n\n{\n\n \nmaster\n:\n \nmaster.mysql.foo.com\n,\n\n \nreadReplicas\n:\n \n[\nread01.mysql.foo.com\n,\n \nread02.mysql.foo.com\n],\n\n \nuser\n:\n \nroot\n,\n\n \npassword\n:\n \npassword\n,\n\n \ndatabase\n:\n \nhello\n\n\n}\n\n\n\n\n\n\n\n\nTip\n\n\nYou can also provide the \nreadReplicas\n as a comma-separated string.\n\n\n\n\nDriver\n\n\nYou can get access to the \nMySQL Driver\n on the droplet.\n\n\nimport\n \nVapor\n\n\nimport\n \nMySQLProvider\n\n\n\nlet\n \nmysqlDriver\n \n=\n \ntry\n \ndrop\n.\nmysql\n()\n\n\n\n\n\n\nConfigure Cache\n\n\nYou can also choose to use your Fluent database (now set to MySQL) for caching. \n\n\nConfig/droplet.json\n\n\n{\n\n \ndriver\n:\n \nfluent\n\n\n}\n\n\n\n\n\n\nLearn more about \ncaching here\n.\n\n\nDone\n\n\nNext time you boot your Droplet, you should see:\n\n\nDatabase prepared\n\n\n\n\n\nYou are now ready to \nstart using Fluent\n with your MySQL database.", - "title": "Provider" - }, - { - "location": "/mysql/provider/#mysql-provider", - "text": "After you've added the MySQL Provider package to your project, setting the provider up in code is easy.", - "title": "MySQL Provider" - }, - { - "location": "/mysql/provider/#add-to-droplet", - "text": "First, register the MySQLProvider.Provider with your Droplet. import Vapor import MySQLProvider let config = try Config () try config . addProvider ( MySQLProvider . Provider . self ) let drop = try Droplet ( config ) ...", - "title": "Add to Droplet" - }, - { - "location": "/mysql/provider/#configure-fluent", - "text": "Once the provider is added to your Droplet, you can configure Fluent to use the MySQL driver. Config/fluent.json { \n driver : mysql } Seealso Learn more about configuration files in the Settings guide .", - "title": "Configure Fluent" - }, - { - "location": "/mysql/provider/#configure-mysql", - "text": "If you run your application now, you will likely see an error that the MySQL configuration file is missing. Let's add that now.", - "title": "Configure MySQL" - }, - { - "location": "/mysql/provider/#basic", - "text": "Here is an example of a simple MySQL configuration file. Config/mysql.json { \n hostname : 127.0.0.1 , \n user : root , \n password : password , \n database : hello } Note It's a good idea to store the MySQL configuration file in the Config/secrets folder since it contains sensitive information.", - "title": "Basic" - }, - { - "location": "/mysql/provider/#url", - "text": "You can also pass the MySQL credentials as a URL. Config/mysql.json { \n url : http://root:password@127.0.0.1/hello }", - "title": "URL" - }, - { - "location": "/mysql/provider/#read-replicas", - "text": "Read replicas can be supplied by passing a single master hostname and an array of readReplicas hostnames. Config/mysql.json { \n master : master.mysql.foo.com , \n readReplicas : [ read01.mysql.foo.com , read02.mysql.foo.com ], \n user : root , \n password : password , \n database : hello } Tip You can also provide the readReplicas as a comma-separated string.", - "title": "Read Replicas" - }, - { - "location": "/mysql/provider/#driver", - "text": "You can get access to the MySQL Driver on the droplet. import Vapor import MySQLProvider let mysqlDriver = try drop . mysql ()", - "title": "Driver" - }, - { - "location": "/mysql/provider/#configure-cache", - "text": "You can also choose to use your Fluent database (now set to MySQL) for caching. Config/droplet.json { \n driver : fluent } Learn more about caching here .", - "title": "Configure Cache" - }, - { - "location": "/mysql/provider/#done", - "text": "Next time you boot your Droplet, you should see: Database prepared You are now ready to start using Fluent with your MySQL database.", - "title": "Done" - }, - { - "location": "/mysql/driver/", - "text": "MySQL Driver\n\n\nFluent uses the MySQL driver to talk to your MySQL database. Although you won't need to use it most of the time, it does \nhave some handy features.\n\n\nRaw\n\n\nSometimes you need to bypass Fluent and send raw queries to the database.\n\n\nlet\n \nresult\n \n=\n \ntry\n \nmysqlDriver\n.\nraw\n(\nSELECT @@version\n)\n\n\n\n\n\n\n!!! note:\n If you are using Vapor, you can get access to the MySQL Driver with \ndrop.mysql()\n\n\nTransaction\n\n\nIf you are performing multiple queries that depend on eachother, you can use transactions to make sure nothing gets saved\nto the database if one of the queries fails.\n\n\ntry\n \nmysqlDriver\n.\ntransaction\n \n{\n \nconn\n \nin\n\n \n// delete user\ns pets, then delete user\n\n \n// if one of these fails, the transaction will rollback\n\n \ntry\n \nuser\n.\npets\n.\nmakeQuery\n(\nconn\n).\ndelete\n()\n\n \ntry\n \nuser\n.\nmakeQuery\n(\nconn\n).\ndelete\n()\n\n\n}\n\n\n\n\n\n\n\n\nWarning\n\n\nMake sure to use the connection supplied to the closure for all queries you want included in the transaction.\n\n\n\n\nManual\n\n\nYou can also manually send a query to the driver without going through Fluent.\n\n\nlet\n \nquery\n \n=\n \ntry\n \nUser\n.\nmakeQuery\n()\n\n\n...\n\n\n\nlet\n \nresults\n \n=\n \ntry\n \nmysqlDriver\n.\nquery\n(\nquery\n)", - "title": "Driver" - }, - { - "location": "/mysql/driver/#mysql-driver", - "text": "Fluent uses the MySQL driver to talk to your MySQL database. Although you won't need to use it most of the time, it does \nhave some handy features.", - "title": "MySQL Driver" - }, - { - "location": "/mysql/driver/#raw", - "text": "Sometimes you need to bypass Fluent and send raw queries to the database. let result = try mysqlDriver . raw ( SELECT @@version ) !!! note:\n If you are using Vapor, you can get access to the MySQL Driver with drop.mysql()", - "title": "Raw" - }, - { - "location": "/mysql/driver/#transaction", - "text": "If you are performing multiple queries that depend on eachother, you can use transactions to make sure nothing gets saved\nto the database if one of the queries fails. try mysqlDriver . transaction { conn in \n // delete user s pets, then delete user \n // if one of these fails, the transaction will rollback \n try user . pets . makeQuery ( conn ). delete () \n try user . makeQuery ( conn ). delete () } Warning Make sure to use the connection supplied to the closure for all queries you want included in the transaction.", - "title": "Transaction" - }, - { - "location": "/mysql/driver/#manual", - "text": "You can also manually send a query to the driver without going through Fluent. let query = try User . makeQuery () ... let results = try mysqlDriver . query ( query )", - "title": "Manual" - }, - { - "location": "/redis/package/", - "text": "Using Redis\n\n\nThis section outlines how to import the Redis package both with or without a Vapor project.\n\n\nWith Vapor\n\n\nThe easiest way to use Redis with Vapor is to include the Redis provider. \n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/vapor.git\n,\n \nmajorVersion\n:\n \n2\n),\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/redis-provider.git\n,\n \nmajorVersion\n:\n \n2\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nThe Redis provider package adds Redis to your project and conforms it to Vapor's \nCacheProtocol\n. \n\n\nUse \nimport RedisProvider\n.\n\n\nJust Redis\n\n\nAt the core of the Redis provider is a pure Swift Redis client. This package can be used by itself to send raw cache queries to your Redis database.\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n...\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/redis.git\n,\n \nmajorVersion\n:\n \n2\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Redis\n.", - "title": "Package" - }, - { - "location": "/redis/package/#using-redis", - "text": "This section outlines how to import the Redis package both with or without a Vapor project.", - "title": "Using Redis" - }, - { - "location": "/redis/package/#with-vapor", - "text": "The easiest way to use Redis with Vapor is to include the Redis provider. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n . Package ( url : https://github.com/vapor/vapor.git , majorVersion : 2 ), \n . Package ( url : https://github.com/vapor/redis-provider.git , majorVersion : 2 ) \n ], \n exclude : [ ... ] ) The Redis provider package adds Redis to your project and conforms it to Vapor's CacheProtocol . Use import RedisProvider .", - "title": "With Vapor" - }, - { - "location": "/redis/package/#just-redis", - "text": "At the core of the Redis provider is a pure Swift Redis client. This package can be used by itself to send raw cache queries to your Redis database. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . Package ( url : https://github.com/vapor/redis.git , majorVersion : 2 ) \n ], \n exclude : [ ... ] ) Use import Redis .", - "title": "Just Redis" - }, - { - "location": "/redis/provider/", - "text": "Redis Provider\n\n\nAfter you've \nadded the Redis Provider package\n to your project, setting the provider up in code is easy.\n\n\nAdd to Droplet\n\n\nFirst, register the \nRedisProvider.Provider\n with your Droplet.\n\n\nimport\n \nVapor\n\n\nimport\n \nRedisProvider\n\n\n\nlet\n \nconfig\n \n=\n \ntry\n \nConfig\n()\n\n\ntry\n \nconfig\n.\naddProvider\n(\nRedisProvider\n.\nProvider\n.\nself\n)\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nconfig\n)\n\n\n\n...\n\n\n\n\n\n\nConfigure Vapor\n\n\nOnce the provider is added to your Droplet, you can configure Vapor to use Redis for caching.\n\n\nConfig/droplet.json\n\n\n{\n\n \ncache\n:\n \nredis\n\n\n}\n\n\n\n\n\n\n\n\nSeealso\n\n\nLearn more about configuration files in the \nSettings guide\n.\n\n\n\n\nConfigure Redis\n\n\nIf you run your application now, you will likely see an error that the Redis configuration file is missing. Let's add that now.\n\n\nBasic\n\n\nHere is an example of a simple Redis configuration file.\n\n\nConfig/redis.json\n\n\n{\n\n \nhostname\n:\n \n127.0.0.1\n,\n\n \nport\n:\n \n6379\n,\n\n \npassword\n:\n \nsecret\n,\n\n \ndatabase\n:\n \n2\n\n\n}\n\n\n\n\n\n\nBoth password and database are optional.\n\n\n\n\nNote\n\n\nIt's a good idea to store the Redis configuration file in the \nConfig/secrets\n folder since it may contain sensitive information.\n\n\n\n\nURL\n\n\nYou can also pass the Redis credentials as a URL.\n\n\nConfig/redis.json\n\n\n{\n\n \nurl\n:\n \nredis://:secret@127.0.0.1:6379/2\n\n\n}\n\n\n\n\n\n\nBoth password and database are optional.\n\n\nDone\n\n\nYou are now ready to \nstart using Cache\n with your Redis database.", - "title": "Provider" - }, - { - "location": "/redis/provider/#redis-provider", - "text": "After you've added the Redis Provider package to your project, setting the provider up in code is easy.", - "title": "Redis Provider" - }, - { - "location": "/redis/provider/#add-to-droplet", - "text": "First, register the RedisProvider.Provider with your Droplet. import Vapor import RedisProvider let config = try Config () try config . addProvider ( RedisProvider . Provider . self ) let drop = try Droplet ( config ) ...", - "title": "Add to Droplet" - }, - { - "location": "/redis/provider/#configure-vapor", - "text": "Once the provider is added to your Droplet, you can configure Vapor to use Redis for caching. Config/droplet.json { \n cache : redis } Seealso Learn more about configuration files in the Settings guide .", - "title": "Configure Vapor" - }, - { - "location": "/redis/provider/#configure-redis", - "text": "If you run your application now, you will likely see an error that the Redis configuration file is missing. Let's add that now.", - "title": "Configure Redis" - }, - { - "location": "/redis/provider/#basic", - "text": "Here is an example of a simple Redis configuration file. Config/redis.json { \n hostname : 127.0.0.1 , \n port : 6379 , \n password : secret , \n database : 2 } Both password and database are optional. Note It's a good idea to store the Redis configuration file in the Config/secrets folder since it may contain sensitive information.", - "title": "Basic" - }, - { - "location": "/redis/provider/#url", - "text": "You can also pass the Redis credentials as a URL. Config/redis.json { \n url : redis://:secret@127.0.0.1:6379/2 } Both password and database are optional.", - "title": "URL" - }, - { - "location": "/redis/provider/#done", - "text": "You are now ready to start using Cache with your Redis database.", - "title": "Done" - }, - { - "location": "/auth/package/", - "text": "Using Auth\n\n\nThis section outlines how to import the Auth package both with or without a Vapor project.\n\n\nWith Vapor\n\n\nThe easiest way to use Auth with Vapor is to include the Auth provider.\n\n\nYou can achieve this by running:\n\n\nvapor provider add auth\n\n\n\n\n\nor by manually modifying your \nPackage.swift\n file:\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/vapor.git\n,\n \nmajorVersion\n:\n \n2\n),\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/auth-provider.git\n,\n \nmajorVersion\n:\n \n1\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nThe Auth provider package adds Auth to your project and adds some additional, vapor-specific conveniences like auth middleware. \n\n\nAfter you added the dependency, fetch it using \nvapor update\n.\n\n\nUsing \nimport AuthProvider\n will import all of the auth middleware and the Authentication and Authorization modules. \n\n\nWithout Vapor\n\n\nAt 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 \nPackage.swift\n files:\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n...\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/auth.git\n,\n \nmajorVersion\n:\n \n1\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nAfter you added the dependency, fetch it using \nvapor update\n.\n\n\nUse \nimport Auth\n to access the core auth classes.", - "title": "Package" - }, - { - "location": "/auth/package/#using-auth", - "text": "This section outlines how to import the Auth package both with or without a Vapor project.", - "title": "Using Auth" - }, - { - "location": "/auth/package/#with-vapor", - "text": "The easiest way to use Auth with Vapor is to include the Auth provider. You can achieve this by running: vapor provider add auth or by manually modifying your Package.swift file: import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n . Package ( url : https://github.com/vapor/vapor.git , majorVersion : 2 ), \n . Package ( url : https://github.com/vapor/auth-provider.git , majorVersion : 1 ) \n ], \n 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.", - "title": "With Vapor" - }, - { - "location": "/auth/package/#without-vapor", - "text": "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: import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . Package ( url : https://github.com/vapor/auth.git , majorVersion : 1 ) \n ], \n exclude : [ ... ] ) After you added the dependency, fetch it using vapor update . Use import Auth to access the core auth classes.", - "title": "Without Vapor" - }, - { - "location": "/auth/provider/", - "text": "Auth Provider\n\n\nAfter you've \nadded the Auth Provider package\n to your project, setting the provider up in code is easy.\n\n\nAdd to Droplet\n\n\nRegister the \nAuthProvider.Provider\n with your Droplet in your \nConfig+Setup.swift\n file:\n\n\nimport\n \nApp\n\n\nimport\n \nAuthProvider\n\n\n\nlet\n \nconfig\n \n=\n \ntry\n \nConfig\n()\n\n\ntry\n \nconfig\n.\naddProvider\n(\nAuthProvider\n.\nProvider\n.\nself\n)\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nconfig\n)\n\n\n\n...\n\n\n\n\n\n\nDone\n\n\nYou are now ready to start using the Auth package.", - "title": "Provider" - }, - { - "location": "/auth/provider/#auth-provider", - "text": "After you've added the Auth Provider package to your project, setting the provider up in code is easy.", - "title": "Auth Provider" - }, - { - "location": "/auth/provider/#add-to-droplet", - "text": "Register the AuthProvider.Provider with your Droplet in your Config+Setup.swift file: import App import AuthProvider let config = try Config () try config . addProvider ( AuthProvider . Provider . self ) let drop = try Droplet ( config ) ...", - "title": "Add to Droplet" - }, - { - "location": "/auth/provider/#done", - "text": "You are now ready to start using the Auth package.", - "title": "Done" - }, - { - "location": "/auth/getting-started/", - "text": "Getting Started\n\n\nVapor's \nAuth Provider\n package makes implementing authentication and \nauthorization easy and secure. It supports common auth patterns such as:\n\n\n\n\nToken (bearer) authentication\n\n\nUsername + password (basic) authentication\n\n\nPermission-based authorization\n\n\nSession-based persistance \n\n\n\n\nAuth's modular, protocol-based nature also makes it a great foundation for custom auth needs.\n\n\n\n\nTip\n\n\nUse \nvapor new \nname\n --template=vapor/auth-template\n to create a new \nproject template\n with AuthProvider and samples included.\n\n\n\n\nPackage\n\n\nTo use Auth, you will need to have the \nAuth Provider\n added to your project.\nThis is as simple as adding the following line to your \nPackage.swift\n file.\n\n\n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/auth-provider.git\n,\n \n...)\n\n\n\n\n\n\nCheck out the \nPackage\n section for more information.\n\n\nExample\n\n\nLet's take a look at how we can implement a simple, token-based authentication system using Vapor and Auth.\n\n\nUser\n\n\nWe will start by creating a model to represent our user. If you already have a user class, you can skip this step.\n\n\nimport\n \nVapor\n\n\nimport\n \nFluentProvider\n\n\n\nfinal\n \nclass\n \nExampleUser\n:\n \nModel\n \n{\n\n \nlet\n \nname\n:\n \nString\n\n\n \n...\n\n\n}\n\n\n\nextension\n \nExampleUser\n:\n \nPreparation\n \n{\n \n...\n \n}\n\n\n\n\n\n\nHere we create a very simple user with just one property: a name.\n\n\n\n\nSeealso\n\n\nWe're omitting most of \nModel\n and \nPreparation\n protocol requirements. Check out Fluent's \n\nGetting Started\n for more information about these protocols.\n\n\n\n\nToken\n\n\nNext let's create a model to represent our authentication tokens. These will be stored in a separate database\ntable or collection called \"tokens\".\n\n\nWhen a user logs in, we will create a new token for them. They will then use this token on subsequent requests\ninstead of their username and password. \n\n\nFor now, here's what our simple token model will look like.\n\n\nimport\n \nVapor\n\n\nimport\n \nFluentProvider\n\n\n\nfinal\n \nclass\n \nExampleToken\n:\n \nModel\n \n{\n\n \nlet\n \ntoken\n:\n \nString\n\n \nlet\n \nuserId\n:\n \nIdentifier\n\n\n \nvar\n \nuser\n:\n \nParent\nExampleToken\n,\n \nExampleUser\n \n{\n\n \nreturn\n \nparent\n(\nid\n:\n \nuserId\n)\n\n \n}\n\n\n \n...\n\n\n}\n\n\n\nextension\n \nExampleToken\n:\n \nPreparation\n \n{\n \n...\n \n}\n\n\n\n\n\n\nThis token has two properties:\n\n\n\n\ntoken: a unique, random string that we will send in requests\n\n\nuserId: the identifier for the user to whom this token belongs\n\n\n\n\n\n\nSeealso\n\n\nWe're using Fluent relations here. Check out Fluent's \nRelations\n\nsection for more information.\n\n\n\n\nToken Authenticatable\n\n\nNow that we have our example user and token, we can make our user authenticatable with the token.\n\n\nThis might sound complicated, but it's actually pretty easy:\n\n\nimport\n \nAuthProvider\n\n\n\nextension\n \nExampleUser\n:\n \nTokenAuthenticatable\n \n{\n\n \n// the token model that should be queried\n\n \n// to authenticate this user\n\n \npublic\n \ntypealias\n \nTokenType\n \n=\n \nExampleToken\n\n\n}\n\n\n\n\n\n\nNow that our example user is \nTokenAuthenticatable\n, we can move on to the next step!\n\n\nUser Helper\n\n\nLet's add a simple convenience method on request for accessing the authenticated user.\n\n\nextension\n \nRequest\n \n{\n\n \nfunc\n \nuser\n()\n \nthrows\n \n-\n \nExampleUser\n \n{\n\n \nreturn\n \ntry\n \nauth\n.\nassertAuthenticated\n()\n\n \n}\n\n\n}\n\n\n\n\n\n\nThis is a nice shortcut that will come in handy in a few steps.\n\n\nMiddleware\n\n\nTo require authentication we need to add the \nTokenAuthenticationMiddleware\n. You can apply this middleware\nto individual routes or to the entire Droplet. For simplicity, we'll apply it to the Droplet.\n\n\nimport\n \nVapor\n\n\nimport\n \nAuthProvider\n\n\nimport\n \nFluentProvider\n\n\n\nlet\n \nconfig\n \n=\n \ntry\n \nConfig\n()\n\n\n\nconfig\n.\npreparations\n.\nappend\n(\nExampleUser\n.\nself\n)\n\n\nconfig\n.\npreparations\n.\nappend\n(\nExampleToken\n.\nself\n)\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nconfig\n)\n\n\n\n\nlet\n \ntokenMiddleware\n \n=\n \nTokenAuthenticationMiddleware\n(\nExampleUser\n.\nself\n)\n\n\n\n/// use this route group for protected routes\n\n\nlet\n \nauthed\n \n=\n \ndrop\n.\ngrouped\n(\ntokenMiddleware\n)\n\n\n\n\n\n\nSince our \nExampleUser\n class is \nTokenAuthenticatable\n, we can pass it into the middleware's init method.\n\n\n\n\nSeealso\n\n\nIf you only want to require authentication for certain routes, look at our \n\nRoute Group\n section in the routing docs.\n\n\n\n\nRoute\n\n\nNow that we have a route group protected by our TokenMiddleware, let's add a route to\nreturn the authenticated user's name.\n\n\nauthed\n.\nget\n(\nme\n)\n \n{\n \nreq\n \nin\n\n \n// return the authenticated user\ns name\n\n \nreturn\n \ntry\n \nreq\n.\nuser\n().\nname\n\n\n}\n\n\n\n\n\n\n\n\nTip\n\n\nWe're using the \n.user()\n convenience we added to \nRequest\n here. It is a shortcut\nfor \nlet user = try req.auth.assertAuthenticated(ExampleUser.self)\n \n\n\n\n\nDatabase\n\n\nThat's it! We now have a functioning authentication system. Let's add a couple of entries\nto our database and test it out.\n\n\nUsers\n\n\n\n\n\n\n\n\nid\n\n\nname\n\n\n\n\n\n\n\n\n\n\n1\n\n\nBob\n\n\n\n\n\n\n\n\nTokens\n\n\n\n\n\n\n\n\nid\n\n\ntoken\n\n\nuser_id\n\n\n\n\n\n\n\n\n\n\n1\n\n\nfoo\n\n\n1\n\n\n\n\n\n\n\n\nRequest\n\n\nNow we can make a request to our Vapor app.\n\n\nGET\n \n/me\n \nHTTP\n/\n1.1\n\n\nAuthorization\n:\n \nBearer foo\n\n\n\n\n\n\nAnd we should get a response like.\n\n\nHTTP\n/\n1.1\n \n200\n \nOK\n\n\nContent-Type\n:\n \ntext/plain\n\n\nBob\n\n\n\n\n\nBad Token\n\n\nTo make sure it's secure, let's test using a token that's not in our database.\n\n\nGET\n \n/me\n \nHTTP\n/\n1.1\n\n\nAuthorization\n:\n \nBearer not-a-token\n\n\n\n\n\n\nAnd we should get a response like.\n\n\nHTTP\n/\n1.1\n \n403\n \nForbidden\n\n\n\n\n\n\nNext Steps\n\n\nTo build this out into a production-ready authentication system, you will need to build some\nadditional routes for creating users and creating tokens. \n\n\nContinue on in the Auth section to learn more about different types of authentication.", - "title": "Getting Started" - }, - { - "location": "/auth/getting-started/#getting-started", - "text": "Vapor's Auth Provider package makes implementing authentication and \nauthorization 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 with AuthProvider and samples included.", - "title": "Getting Started" - }, - { - "location": "/auth/getting-started/#package", - "text": "To use Auth, you will need to have the Auth Provider added to your project.\nThis is as simple as adding the following line to your Package.swift file. . Package ( url : https://github.com/vapor/auth-provider.git , ...) Check out the Package section for more information.", - "title": "Package" - }, - { - "location": "/auth/getting-started/#example", - "text": "Let's take a look at how we can implement a simple, token-based authentication system using Vapor and Auth.", - "title": "Example" - }, - { - "location": "/auth/getting-started/#user", - "text": "We will start by creating a model to represent our user. If you already have a user class, you can skip this step. import Vapor import FluentProvider final class ExampleUser : Model { \n let name : String \n\n ... } 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 for more information about these protocols.", - "title": "User" - }, - { - "location": "/auth/getting-started/#token", - "text": "Next let's create a model to represent our authentication tokens. These will be stored in a separate database\ntable 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\ninstead of their username and password. For now, here's what our simple token model will look like. import Vapor import FluentProvider final class ExampleToken : Model { \n let token : String \n let userId : Identifier \n\n var user : Parent ExampleToken , ExampleUser { \n return parent ( id : userId ) \n } \n\n ... } 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 \nsection for more information.", - "title": "Token" - }, - { - "location": "/auth/getting-started/#token-authenticatable", - "text": "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: import AuthProvider extension ExampleUser : TokenAuthenticatable { \n // the token model that should be queried \n // to authenticate this user \n public typealias TokenType = ExampleToken } Now that our example user is TokenAuthenticatable , we can move on to the next step!", - "title": "Token Authenticatable" - }, - { - "location": "/auth/getting-started/#user-helper", - "text": "Let's add a simple convenience method on request for accessing the authenticated user. extension Request { \n func user () throws - ExampleUser { \n return try auth . assertAuthenticated () \n } } This is a nice shortcut that will come in handy in a few steps.", - "title": "User Helper" - }, - { - "location": "/auth/getting-started/#middleware", - "text": "To require authentication we need to add the TokenAuthenticationMiddleware . You can apply this middleware\nto individual routes or to the entire Droplet. For simplicity, we'll apply it to the Droplet. 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 section in the routing docs.", - "title": "Middleware" - }, - { - "location": "/auth/getting-started/#route", - "text": "Now that we have a route group protected by our TokenMiddleware, let's add a route to\nreturn the authenticated user's name. authed . get ( me ) { req in \n // return the authenticated user s name \n return try req . user (). name } Tip We're using the .user() convenience we added to Request here. It is a shortcut\nfor let user = try req.auth.assertAuthenticated(ExampleUser.self)", - "title": "Route" - }, - { - "location": "/auth/getting-started/#database", - "text": "That's it! We now have a functioning authentication system. Let's add a couple of entries\nto our database and test it out.", - "title": "Database" - }, - { - "location": "/auth/getting-started/#users", - "text": "id name 1 Bob", - "title": "Users" - }, - { - "location": "/auth/getting-started/#tokens", - "text": "id token user_id 1 foo 1", - "title": "Tokens" - }, - { - "location": "/auth/getting-started/#request", - "text": "Now we can make a request to our Vapor app. GET /me HTTP / 1.1 Authorization : Bearer foo And we should get a response like. HTTP / 1.1 200 OK Content-Type : text/plain \n\nBob", - "title": "Request" - }, - { - "location": "/auth/getting-started/#bad-token", - "text": "To make sure it's secure, let's test using a token that's not in our database. GET /me HTTP / 1.1 Authorization : Bearer not-a-token And we should get a response like. HTTP / 1.1 403 Forbidden", - "title": "Bad Token" - }, - { - "location": "/auth/getting-started/#next-steps", - "text": "To build this out into a production-ready authentication system, you will need to build some\nadditional routes for creating users and creating tokens. Continue on in the Auth section to learn more about different types of authentication.", - "title": "Next Steps" - }, - { - "location": "/auth/helper/", - "text": "Auth Helper\n\n\nThe Auth package adds a convenience property on every request that makes it \neasy to authenticate, persist, and unauthenticate users.\n\n\nAuthentication\n\n\nChecking\n\n\nYou can get the currently authenticated user.\n\n\nlet\n \nuser\n \n=\n \nreq\n.\nauth\n.\nauthenticated\n(\nUser\n.\nself\n)\n\n\n\n\n\n\nYou can check to see if the user is authenticated.\n\n\nif\n \nreq\n.\nauth\n.\nisAuthenticated\n(\nUser\n.\nself\n)\n \n{\n\n \n...\n \n\n}\n\n\n\n\n\n\nYou can also assert that the user is authenticated.\n\n\nlet\n \nuser\n \n=\n \ntry\n \nreq\n.\nauth\n.\nassertAuthenticated\n(\nUser\n.\nself\n)\n\n\n\n\n\n\n!!! note:\n A 403 Forbidden error will be thrown if the user is not authenticated.\n\n\nManual\n\n\nYou can manually authenticate a user.\n\n\nif\n \nlet\n \nuser\n \n=\n \ntry\n \nUser\n.\nfind\n(\n1\n)\n \n{\n\n \nreq\n.\nauth\n.\nauthenticate\n(\nuser\n)\n \n\n}\n\n\n\n\n\n\nYou can also unauthenticate the currently authenticated user.\n\n\ntry\n \nreq\n.\nauth\n.\nunauthenticate\n()\n\n\n\n\n\n\n!!! note:\n If the user is \nPersistable\n, they will also be unpersisted.\n\n\nHeaders\n\n\nThe helper can be used to access common authorization headers.\n\n\nprint\n(\nreq\n.\nauth\n.\nheader\n)\n\n\n\n\n\n\nToken\n\n\nThe header has additional conveniences for parsing out bearer tokens.\n\n\nprint\n(\nreq\n.\nauth\n.\nheader\n?.\nbearer\n)\n\n\n\n\n\n\n\n\nTip\n\n\nYou can use \n_authorizationBasic\n and \n_authorizationBearer\n to send tokens in the URL string.\n\n\n\n\nPassword\n\n\nAnd basic auth username + password.\n\n\nprint\n(\nreq\n.\nauth\n.\nheader\n?.\nbasic\n)", - "title": "Helper" - }, - { - "location": "/auth/helper/#auth-helper", - "text": "The Auth package adds a convenience property on every request that makes it \neasy to authenticate, persist, and unauthenticate users.", - "title": "Auth Helper" - }, - { - "location": "/auth/helper/#authentication", - "text": "", - "title": "Authentication" - }, - { - "location": "/auth/helper/#checking", - "text": "You can get the currently authenticated user. let user = req . auth . authenticated ( User . self ) You can check to see if the user is authenticated. if req . auth . isAuthenticated ( User . self ) { \n ... } You can also assert that the user is authenticated. let user = try req . auth . assertAuthenticated ( User . self ) !!! note:\n A 403 Forbidden error will be thrown if the user is not authenticated.", - "title": "Checking" - }, - { - "location": "/auth/helper/#manual", - "text": "You can manually authenticate a user. if let user = try User . find ( 1 ) { \n req . auth . authenticate ( user ) } You can also unauthenticate the currently authenticated user. try req . auth . unauthenticate () !!! note:\n If the user is Persistable , they will also be unpersisted.", - "title": "Manual" - }, - { - "location": "/auth/helper/#headers", - "text": "The helper can be used to access common authorization headers. print ( req . auth . header )", - "title": "Headers" - }, - { - "location": "/auth/helper/#token", - "text": "The header has additional conveniences for parsing out bearer tokens. print ( req . auth . header ?. bearer ) Tip You can use _authorizationBasic and _authorizationBearer to send tokens in the URL string.", - "title": "Token" - }, - { - "location": "/auth/helper/#password", - "text": "And basic auth username + password. print ( req . auth . header ?. basic )", - "title": "Password" - }, - { - "location": "/auth/password/", - "text": "Username + Password (Basic) Auth\n\n\nThe \nAuthorization: Basic ...\n header can be used to send username and password credentials\nfor authentication.\n\n\nThis page will show you how to use this type of authentication in your web app.\n\n\n!!! note:\n Sending and storing passwords should be avoided wherever possible. Use tokens or\n sessions persistance to prevent the need for sending the password in every request.\n\n\nPassword Authenticatable\n\n\nStart by conforming your user model to the \nPasswordAuthenticatable\n protocol.\n\n\nimport\n \nAuthProvider\n\n\n\nextension\n \nUser\n:\n \nPasswordAuthenticatable\n \n{\n \n}\n\n\n\n\n\n\nCustom\n\n\nIf your user conforms to \nModel\n, all of the required methods will be implemented automatically. However,\nyou can implement them if you want to do something custom.\n\n\nextension\n \nUser\n:\n \nPasswordAuthenticatable\n \n{\n\n \n/// Return the user matching the supplied\n\n \n/// username and password\n\n \nstatic\n \nfunc\n \nauthenticate\n(\n_\n:\n \nPassword\n)\n \nthrows\n \n-\n \nSelf\n \n{\n\n \n// something custom\n\n \n}\n\n\n \n/// The entity\ns hashed password used for\n\n \n/// validating against Password credentials\n\n \n/// with a PasswordVerifier\n\n \nvar\n \nhashedPassword\n:\n \nString\n?\n \n{\n\n \n// something custom\n\n \n}\n\n\n \n/// The key under which the user\ns username,\n\n \n/// email, or other identifing value is stored.\n\n \nstatic\n \nvar\n \nusernameKey\n:\n \nString\n \n{\n\n \n// something custom\n\n \n}\n\n\n \n/// The key under which the user\ns password\n\n \n/// is stored.\n\n \nstatic\n \nvar\n \npasswordKey\n:\n \nString\n \n{\n\n \n// something custom\n\n \n}\n\n\n \n/// Optional password verifier to use when\n\n \n/// comparing plaintext passwords from the \n\n \n/// Authorization header to hashed passwords\n\n \n/// in the database.\n\n \nstatic\n \nvar\n \npasswordVerifier\n:\n \nPasswordVerifier\n?\n \n{\n\n \n// some hasher\n\n \n}\n\n\n}\n\n\n\n\n\n\nMiddleware\n\n\nOnce your model conforms to the \nPasswordAuthenticatable\n protocol, you can create the middleware.\n\n\nimport\n \nVapor\n\n\nimport\n \nAuthProvider\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n()\n\n\n\nlet\n \npasswordMiddleware\n \n=\n \nPasswordAuthenticationMiddleware\n(\nUser\n.\nself\n)\n\n\n\nlet\n \nauthed\n \n=\n \ntry\n \ndrop\n.\ngrouped\n(\npasswordMiddleware\n)\n\n\n\ntry\n \ndrop\n.\nrun\n()\n\n\n\n\n\n\nAll routes added to the \nauthed\n route group will be protected by the password middleware.\n\n\n\n\nSeealso\n\n\nIf you only want to globally require the password middleware, checkout the\n\nMiddleware Config\n section in the HTTP docs.\n\n\n\n\nRoute\n\n\nNow you can add a route to return the authenticated user.\n\n\nauthed\n.\nget\n(\nme\n)\n \n{\n \nreq\n \nin\n\n \n// return the authenticated user\n\n \nreturn\n \ntry\n \nreq\n.\nauth\n.\nassertAuthenticated\n(\nUser\n.\nself\n)\n\n\n}\n\n\n\n\n\n\nCall \nreq.user.authenticated(User.self)\n to get access to the authenticated user.\n\n\nRequest\n\n\nNow we can make a request to our Vapor app.\n\n\nGET\n \n/me\n \nHTTP\n/\n1.1\n\n\nAuthorization\n:\n \nBasic dmFwb3I6Zm9v \n\n\n\n\n\n\n\n\nNote\n\n\ndmFwb3I6Zm9v\n is \"vapor:foo\" base64 encoded where \"vapor\" is the username and \n\"foo\" is the password. This is the format of Basic authorization headers.\n\n\n\n\nAnd we should get a response like.\n\n\nHTTP\n/\n1.1\n \n200\n \nOK\n\n\nContent-Type\n:\n \ntext/plain\n\n\nVapor", - "title": "Password" - }, - { - "location": "/auth/password/#username-password-basic-auth", - "text": "The Authorization: Basic ... header can be used to send username and password credentials\nfor authentication. This page will show you how to use this type of authentication in your web app. !!! note:\n Sending and storing passwords should be avoided wherever possible. Use tokens or\n sessions persistance to prevent the need for sending the password in every request.", - "title": "Username + Password (Basic) Auth" - }, - { - "location": "/auth/password/#password-authenticatable", - "text": "Start by conforming your user model to the PasswordAuthenticatable protocol. import AuthProvider extension User : PasswordAuthenticatable { }", - "title": "Password Authenticatable" - }, - { - "location": "/auth/password/#custom", - "text": "If your user conforms to Model , all of the required methods will be implemented automatically. However,\nyou can implement them if you want to do something custom. extension User : PasswordAuthenticatable { \n /// Return the user matching the supplied \n /// username and password \n static func authenticate ( _ : Password ) throws - Self { \n // something custom \n } \n\n /// The entity s hashed password used for \n /// validating against Password credentials \n /// with a PasswordVerifier \n var hashedPassword : String ? { \n // something custom \n } \n\n /// The key under which the user s username, \n /// email, or other identifing value is stored. \n static var usernameKey : String { \n // something custom \n } \n\n /// The key under which the user s password \n /// is stored. \n static var passwordKey : String { \n // something custom \n } \n\n /// Optional password verifier to use when \n /// comparing plaintext passwords from the \n /// Authorization header to hashed passwords \n /// in the database. \n static var passwordVerifier : PasswordVerifier ? { \n // some hasher \n } }", - "title": "Custom" - }, - { - "location": "/auth/password/#middleware", - "text": "Once your model conforms to the PasswordAuthenticatable protocol, you can create the middleware. 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 section in the HTTP docs.", - "title": "Middleware" - }, - { - "location": "/auth/password/#route", - "text": "Now you can add a route to return the authenticated user. authed . get ( me ) { req in \n // return the authenticated user \n return try req . auth . assertAuthenticated ( User . self ) } Call req.user.authenticated(User.self) to get access to the authenticated user.", - "title": "Route" - }, - { - "location": "/auth/password/#request", - "text": "Now we can make a request to our Vapor app. GET /me HTTP / 1.1 Authorization : Basic dmFwb3I6Zm9v Note dmFwb3I6Zm9v is \"vapor:foo\" base64 encoded where \"vapor\" is the username and \n\"foo\" is the password. This is the format of Basic authorization headers. And we should get a response like. HTTP / 1.1 200 OK Content-Type : text/plain \n\nVapor", - "title": "Request" - }, - { - "location": "/auth/persist/", - "text": "Persisting Auth\n\n\nPersisting authentication means that a user does not need to provide their credentials with every request.\nThis is useful for web apps where a user should only have to log in once. \n\n\n\n\nNote\n\n\nFor APIs, it's recommended that the user send a token with every request.\nSee \nGetting Started\n for an example about Token auth.\n\n\n\n\nSessions\n\n\nSessions are built into Vapor by default and are an easy way to persist users in your web app.\n\n\nSessionPersistable\n\n\nThe first step is to conform your user model to the \nSessionPersistable\n protocol.\n\n\nimport\n \nAuthProvider\n\n\n\nextension\n \nUser\n:\n \nSessionPersistable\n \n{}\n\n\n\n\n\n\nIf your user is a Model, the protocol methods will be implemented automatically. However,\nyou can implement them if you want to do something custom.\n\n\nimport\n \nAuthProvider\n\n\nimport\n \nHTTP\n\n\n\nextension\n \nUser\n:\n \nSessionPersistable\n \n{\n\n \nfunc\n \npersist\n(\nfor\n:\n \nRequest\n)\n \nthrows\n \n{\n\n \n// something custom\n\n \n}\n\n\n \nstatic\n \nfunc\n \nfetchPersisted\n(\nfor\n:\n \nRequest\n)\n \nthrows\n \n-\n \nSelf\n?\n \n{\n\n \n// something custom\n\n \n}\n\n\n}\n\n\n\n\n\n\nMiddleware\n\n\nNow that the user is \nSessionPersistable\n, we can create our middleware.\n\n\nSessions\n\n\nFirst let's start by creating \nSessionsMiddleware\n. We'll use the \nMemorySessions()\n to get started.\n\n\nlet\n \nmemory\n \n=\n \nMemorySessions\n()\n\n\nlet\n \nsessionsMiddleware\n \n=\n \nSessionsMiddleware\n(\nmemory\n)\n\n\n\n\n\n\nPersist\n\n\nNow let's create the \nPersistMiddleware\n. This will take care of persisting our user once they've \nbeen authenticated. \n\n\nlet\n \npersistMiddleware\n \n=\n \nPersistMiddleware\n(\nUser\n.\nself\n)\n\n\n\n\n\n\nSince our user conforms to \nSessionPersistable\n (and thus \nPersistable\n), we can pass it \ninto this middleware's init.\n\n\nAuthentication\n\n\nNow to create the authentication middleware of your choice. We'll use \nPasswordAuthenticationMiddleware\n\nwhich requires an \nAuthorization: Basic ...\n header with the user's username and password.\n\n\nlet\n \npasswordMiddleware\n \n=\n \nPasswordAuthenticationMiddleware\n(\nUser\n.\nself\n)\n\n\n\n\n\n\n!!! note:\n \nUser\n must conform to \nPasswordAuthenticatable\n to be used with this middleware.\n See the \nPassword\n section to learn more.\n\n\nDroplet\n\n\nNow we can create a Droplet and add all of our middleware.\n\n\nimport\n \nVapor\n\n\nimport\n \nSessions\n\n\nimport\n \nAuthProvider\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n()\n\n\n\nlet\n \nauthed\n \n=\n \ndrop\n.\ngrouped\n([\nsessionsMiddleware\n,\n \npersistMiddleware\n,\n \npasswordMiddleware\n])\n\n\n\n\n\n\n\n\nSeealso\n\n\nIf you only want to globally require the password middleware, checkout the\n\nMiddleware Config\n section in the HTTP docs.\n\n\n\n\nRoute\n\n\nNow you can add a route to return the authenticated user.\n\n\nauthed\n.\nget\n(\nme\n)\n \n{\n \nreq\n \nin\n\n \n// return the authenticated user\n\n \nreturn\n \ntry\n \nreq\n.\nauth\n.\nassertAuthenticated\n(\nUser\n.\nself\n)\n\n\n}\n\n\n\n\n\n\nRequest\n\n\nNow we can make a request to our Vapor app.\n\n\nGET\n \n/me\n \nHTTP\n/\n1.1\n\n\nAuthorization\n:\n \nBasic dmFwb3I6Zm9v \n\n\n\n\n\n\n\n\nNote\n\n\ndmFwb3I6Zm9v\n is \"vapor:foo\" base64 encoded where \"vapor\" is the username and \n\"foo\" is the password. This is the format of Basic authorization headers.\n\n\n\n\nAnd we should get a response like.\n\n\nHTTP\n/\n1.1\n \n200\n \nOK\n\n\nContent-Type\n:\n \ntext/plain\n\n\nSet-Cookie\n:\n \nvapor-session=...\n\n\nVapor\n\n\n\n\n\nNotice the \nvapor-session\n in the response. This can be used in subsequent requests instead of \nthe username and password.", - "title": "Persist" - }, - { - "location": "/auth/persist/#persisting-auth", - "text": "Persisting authentication means that a user does not need to provide their credentials with every request.\nThis 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.\nSee Getting Started for an example about Token auth.", - "title": "Persisting Auth" - }, - { - "location": "/auth/persist/#sessions", - "text": "Sessions are built into Vapor by default and are an easy way to persist users in your web app.", - "title": "Sessions" - }, - { - "location": "/auth/persist/#sessionpersistable", - "text": "The first step is to conform your user model to the SessionPersistable protocol. import AuthProvider extension User : SessionPersistable {} If your user is a Model, the protocol methods will be implemented automatically. However,\nyou can implement them if you want to do something custom. import AuthProvider import HTTP extension User : SessionPersistable { \n func persist ( for : Request ) throws { \n // something custom \n } \n\n static func fetchPersisted ( for : Request ) throws - Self ? { \n // something custom \n } }", - "title": "SessionPersistable" - }, - { - "location": "/auth/persist/#middleware", - "text": "Now that the user is SessionPersistable , we can create our middleware.", - "title": "Middleware" - }, - { - "location": "/auth/persist/#sessions_1", - "text": "First let's start by creating SessionsMiddleware . We'll use the MemorySessions() to get started. let memory = MemorySessions () let sessionsMiddleware = SessionsMiddleware ( memory )", - "title": "Sessions" - }, - { - "location": "/auth/persist/#persist", - "text": "Now let's create the PersistMiddleware . This will take care of persisting our user once they've \nbeen authenticated. let persistMiddleware = PersistMiddleware ( User . self ) Since our user conforms to SessionPersistable (and thus Persistable ), we can pass it \ninto this middleware's init.", - "title": "Persist" - }, - { - "location": "/auth/persist/#authentication", - "text": "Now to create the authentication middleware of your choice. We'll use PasswordAuthenticationMiddleware \nwhich requires an Authorization: Basic ... header with the user's username and password. let passwordMiddleware = PasswordAuthenticationMiddleware ( User . self ) !!! note:\n User must conform to PasswordAuthenticatable to be used with this middleware.\n See the Password section to learn more.", - "title": "Authentication" - }, - { - "location": "/auth/persist/#droplet", - "text": "Now we can create a Droplet and add all of our middleware. 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 section in the HTTP docs.", - "title": "Droplet" - }, - { - "location": "/auth/persist/#route", - "text": "Now you can add a route to return the authenticated user. authed . get ( me ) { req in \n // return the authenticated user \n return try req . auth . assertAuthenticated ( User . self ) }", - "title": "Route" - }, - { - "location": "/auth/persist/#request", - "text": "Now we can make a request to our Vapor app. GET /me HTTP / 1.1 Authorization : Basic dmFwb3I6Zm9v Note dmFwb3I6Zm9v is \"vapor:foo\" base64 encoded where \"vapor\" is the username and \n\"foo\" is the password. This is the format of Basic authorization headers. And we should get a response like. HTTP / 1.1 200 OK Content-Type : text/plain Set-Cookie : vapor-session=... \n\nVapor Notice the vapor-session in the response. This can be used in subsequent requests instead of \nthe username and password.", - "title": "Request" - }, - { - "location": "/auth/redirect-middleware/", - "text": "Redirect Middlewares\n\n\nIncluded in the \nAuthProvider\n package are \nRedirectMiddleware\n and \nInverseRedirectMiddleware\n classes that will help you \nredirect unauthenticated or authenticated requests to a given path. This is especially useful for redirecting users away from secure\npages to a login page and vice versa.\n\n\nRedirect Middleware\n\n\nLet's take a look at how to add a \nRedirectMiddleware\n to your application.\n\n\nExisting Auth\n\n\nSince we only want this middleware to apply to secure pages, we'll apply it using route groups.\n\n\nYou should already have a protected area in your application using one of the authentication middlewares.\n\n\nimport\n \nVapor\n\n\nimport\n \nAuthProvider\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n()\n\n\n\ndrop\n.\nget\n(\nlogin\n)\n \n{\n \nreq\n \nin\n\n \nreturn\n \n// some login form\n\n\n}\n\n\n\nlet\n \nauth\n \n=\n \nTokenAuthenticationMiddleware\n(\nUser\n.\nself\n)\n\n\nlet\n \nprotected\n \n=\n \ndrop\n.\ngrouped\n([\nauth\n])\n\n\nprotected\n.\nget\n(\nsecure\n)\n \n{\n \nreq\n \nin\n\n \nlet\n \nuser\n \n=\n \ntry\n \nreq\n.\nauth\n.\nassertAuthenticated\n(\nUser\n.\nself\n)\n\n \nreturn\n \nWelcome to the secure page, \n\\(\nuser\n.\nname\n)\n\n\n}\n\n\n\n\n\n\nThe above snippet protects access to the page at \nGET /secure\n using the \nTokenAuthenticationMiddleware\n. \n\n\nSince we've applied \nTokenAuthenticationMiddleware\n, this page cannot be accessed by anyone not authenticated.\nAlthough this is perfectly secure, we should provide a better experience for unauthenticated users. Instead of \njust showing them an error message, we can redirect them to the login page.\n\n\nAdd Redirect\n\n\nCreating a redirect middleware is very simple. We'll use one of the presets for redirecting a user to \n/login\n.\n\n\nlet\n \nredirect\n \n=\n \nRedirectMiddleware\n.\nlogin\n()\n\n\n\n\n\n\nNow we just need to add this redirect middleware to our \nprotected\n route group mentioned previously.\n\n\nlet\n \nprotected\n \n=\n \ndrop\n.\ngrouped\n([\nredirect\n,\n \nauth\n])\n\n\n\n\n\n\n\n\nWarning\n\n\nMake sure the redirect middleware comes \nbefore\n the auth middleware. \n\n\n\n\nComplete Example\n\n\nNow whenever an unauthenticated user attemps to visit \nGET /secure\n, they will be redirected to \nGET /login\n.\n\n\nimport\n \nVapor\n\n\nimport\n \nAuthProvider\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n()\n\n\n\nlet\n \nredirect\n \n=\n \nRedirectMiddleware\n.\nlogin\n()\n\n\nlet\n \nauth\n \n=\n \nTokenAuthenticationMiddleware\n(\nTestUser\n.\nself\n)\n\n\n\nlet\n \nprotected\n \n=\n \ndrop\n.\ngrouped\n([\nredirect\n,\n \nauth\n])\n\n\nprotected\n.\nget\n \n{\n \nreq\n \nin\n\n \nlet\n \nuser\n \n=\n \ntry\n \nreq\n.\nauth\n.\nassertAuthenticated\n(\nTestUser\n.\nself\n)\n\n \nreturn\n \nWelcome to the dashboard, \n\\(\nuser\n.\nname\n)\n\n\n}\n\n\n\n\n\n\nCustom Route\n\n\nIf your login page is not \n/login\n or you'd like the redirect middleware to redirect to a different type of page, \nsimply use the full initializer.\n\n\nlet\n \nredirect\n \n=\n \nRedirectMiddleware\n(\npath\n:\n \n/foo\n)\n\n\n\n\n\n\nInverse Redirect Middleware\n\n\nComplementary to the \nRedirectMiddleware\n is the \nInverseRedirectMiddleware\n. Just like you want to redirect unauthenticated\nusers away from secure pages, you also might want to redirect \nauthenticated\n users away from certain pages.\n\n\nFor example, if a user is already authenticated and they visit the login page, they might be confused and attempt to login again.\n\n\nExample\n\n\nHere is an example of the \nInverseRedirectMiddleware\n being used to redirect authenticated \nUser\ns away from the login page.\n\n\nWe are using the preset \n.home()\n convenience, which redirects the user to \nGET /\n.\n\n\nimport\n \nVapor\n\n\nimport\n \nAuthProvider\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n()\n\n\n\nlet\n \nredirect\n \n=\n \nInverseRedirectMiddleware\n.\nhome\n(\nUser\n.\nself\n)\n\n\nlet\n \ngroup\n \n=\n \ndrop\n.\ngrouped\n([\nredirect\n])\n\n\ngroup\n.\nget\n(\nlogin\n)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nPlease login\n\n\n}\n\n\n\n\n\n\nCustom Route\n\n\nIf your desired page is not \n/\n or you'd like the inverse redirect middleware to redirect to a different type of page, \nsimply use the full initializer.\n\n\nlet\n \nredirect\n \n=\n \nInverseRedirectMiddleware\n(\nUser\n.\nself\n,\n \npath\n:\n \n/foo\n)", - "title": "Redirect Middleware" - }, - { - "location": "/auth/redirect-middleware/#redirect-middlewares", - "text": "Included in the AuthProvider package are RedirectMiddleware and InverseRedirectMiddleware classes that will help you \nredirect unauthenticated or authenticated requests to a given path. This is especially useful for redirecting users away from secure\npages to a login page and vice versa.", - "title": "Redirect Middlewares" - }, - { - "location": "/auth/redirect-middleware/#redirect-middleware", - "text": "Let's take a look at how to add a RedirectMiddleware to your application.", - "title": "Redirect Middleware" - }, - { - "location": "/auth/redirect-middleware/#existing-auth", - "text": "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. import Vapor import AuthProvider let drop = try Droplet () drop . get ( login ) { req in \n return // some login form } let auth = TokenAuthenticationMiddleware ( User . self ) let protected = drop . grouped ([ auth ]) protected . get ( secure ) { req in \n let user = try req . auth . assertAuthenticated ( User . self ) \n 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.\nAlthough this is perfectly secure, we should provide a better experience for unauthenticated users. Instead of \njust showing them an error message, we can redirect them to the login page.", - "title": "Existing Auth" - }, - { - "location": "/auth/redirect-middleware/#add-redirect", - "text": "Creating a redirect middleware is very simple. We'll use one of the presets for redirecting a user to /login . let redirect = RedirectMiddleware . login () Now we just need to add this redirect middleware to our protected route group mentioned previously. let protected = drop . grouped ([ redirect , auth ]) Warning Make sure the redirect middleware comes before the auth middleware.", - "title": "Add Redirect" - }, - { - "location": "/auth/redirect-middleware/#complete-example", - "text": "Now whenever an unauthenticated user attemps to visit GET /secure , they will be redirected to GET /login . 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 \n let user = try req . auth . assertAuthenticated ( TestUser . self ) \n return Welcome to the dashboard, \\( user . name ) }", - "title": "Complete Example" - }, - { - "location": "/auth/redirect-middleware/#custom-route", - "text": "If your login page is not /login or you'd like the redirect middleware to redirect to a different type of page, \nsimply use the full initializer. let redirect = RedirectMiddleware ( path : /foo )", - "title": "Custom Route" - }, - { - "location": "/auth/redirect-middleware/#inverse-redirect-middleware", - "text": "Complementary to the RedirectMiddleware is the InverseRedirectMiddleware . Just like you want to redirect unauthenticated\nusers 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.", - "title": "Inverse Redirect Middleware" - }, - { - "location": "/auth/redirect-middleware/#example", - "text": "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 / . import Vapor import AuthProvider let drop = try Droplet () let redirect = InverseRedirectMiddleware . home ( User . self ) let group = drop . grouped ([ redirect ]) group . get ( login ) { req in \n return Please login }", - "title": "Example" - }, - { - "location": "/auth/redirect-middleware/#custom-route_1", - "text": "If your desired page is not / or you'd like the inverse redirect middleware to redirect to a different type of page, \nsimply use the full initializer. let redirect = InverseRedirectMiddleware ( User . self , path : /foo )", - "title": "Custom Route" - }, - { - "location": "/jwt/package/", - "text": "Using JWT\n\n\nThis section outlines how to import the JWT package both with or without a Vapor project.\n\n\nWith Vapor\n\n\nThe easiest way to use JWT with Vapor is to include the JWT provider. \n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/vapor.git\n,\n \nmajorVersion\n:\n \n2\n),\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/jwt-provider.git\n,\n \nmajorVersion\n:\n \n1\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nThe JWT provider package adds JWT to your project and adds some additional, Vapor-specific conveniences like \ndrop.signers\n. \n\n\nUse \nimport JWTProvider\n.\n\n\nJust JWT\n\n\nAt the core of the JWT provider is a fast, pure-Swift JWT implementation for parsing, serializing, and verifying JSON Web Tokens.\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n...\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/jwt.git\n,\n \nmajorVersion\n:\n \n2\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport JWT\n to access the \nJWT\n class.", - "title": "Package" - }, - { - "location": "/jwt/package/#using-jwt", - "text": "This section outlines how to import the JWT package both with or without a Vapor project.", - "title": "Using JWT" - }, - { - "location": "/jwt/package/#with-vapor", - "text": "The easiest way to use JWT with Vapor is to include the JWT provider. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n . Package ( url : https://github.com/vapor/vapor.git , majorVersion : 2 ), \n . Package ( url : https://github.com/vapor/jwt-provider.git , majorVersion : 1 ) \n ], \n exclude : [ ... ] ) The JWT provider package adds JWT to your project and adds some additional, Vapor-specific conveniences like drop.signers . Use import JWTProvider .", - "title": "With Vapor" - }, - { - "location": "/jwt/package/#just-jwt", - "text": "At the core of the JWT provider is a fast, pure-Swift JWT implementation for parsing, serializing, and verifying JSON Web Tokens. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . Package ( url : https://github.com/vapor/jwt.git , majorVersion : 2 ) \n ], \n exclude : [ ... ] ) Use import JWT to access the JWT class.", - "title": "Just JWT" - }, - { - "location": "/jwt/overview/", - "text": "JWT Overview\n\n\nThis guide gives an overview of using the JWT provider package.\n\n\nConfiguration\n\n\nJWTProvider\n can be configured in \n3\n different ways:\n\n\n\n\nCustom signers defined in \njwt.json\n\n\nSupports (private/public): \nhmac\n, \nrsa\n, \nesdca\n.\n\n\n\n\n\n\nLegacy custom signer defined in \njwt.json\n. \n\n\nSupports (private/public): \nhmac\n, \nrsa\n, \nesdca\n.\n\n\n\n\n\n\nRemote JSON Web Key Set (\njwks.json\n) URL\n\n\nSupports (private/public): \nrsa\n.\n\n\n\n\n\n\n\n\nIf your Vapor app is acting as an Authentication Provider, you may want to use either the \nLegacy custom signer\n setup, or the \nCustom signers\n setup, which is great if you want to perform certificates rotation.\n\n\nThe only difference is that with \nCustom signers\n the \nkid\n value in the \nJWT\n header is not ignored, and it must match an associated signer in order to verify the signature.\n\n\nIf your Vapor app is a Resource Provider that delegates Authentication to a 3rd party (auth0, stormpath, etc), you may want to use the \nRemote JSON Web Key Set\n setup. In this configuration the JWT token is generated by a 3rd party that provides the public key in JSON Web Key Set format.\nThe Vapor app is only in charge to verify the \nJWT\n signature using the key set provided by the 3rd party.\n\n\nRemote JSON Web Key Set\n\n\nConfig/jwt.json\n\n\n{\n\n \njwks-url\n:\n \nhttp://my-domain.com/well-known/jwks.json\n\n\n}\n\n\n\n\n\n\nCustom Signers\n\n\nThis allows to specify an array of signers and is particularly useful for rotating certificates.\nCustom signers are not backward compatible and must specify an additional \nkid\n in the configuration.\n\n\n\n\ntype: \nunsigned\n, \nhmac\n, \nrsa\n, \nesdca\n\n\nkid: an unique identifier\n\n\nalgorithm:\n\n\ntype[\nhmac\n]: \nhs256\n, \nhs384\n, \nhs512\n\n\ntype[\nrsa\n]: \nrs256\n, \nrs384\n, \nrs512\n\n\ntype[\nesdca\n]: \nes256\n, \nes384\n, \nes512\n\n\n\n\n\n\n\n\nConfig/jwt.json\n\n\n{\n\n \nsigners\n:\n \n{\n\n \n1234\n:\n \n{\n\n \ntype\n:\n \nrsa\n,\n\n \nalgorithm\n:\n \nrs256\n,\n\n \nkey\n:\n \nyourkeyhere\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nLegacy Custom Signer\n\n\nThis is backwards compatible with the previous implementation.\n\n\n\n\ntype: \nunsigned\n, \nhmac\n, \nrsa\n, \nesdca\n\n\nalgorithm:\n\n\ntype[\nhmac\n]: \nhs256\n, \nhs384\n, \nhs512\n\n\ntype[\nrsa\n]: \nrs256\n, \nrs384\n, \nrs512\n\n\ntype[\nesdca\n]: \nes256\n, \nes384\n, \nes512\n\n\n\n\n\n\n\n\nConfig/jwt.json\n\n\n{\n\n \nsigner\n:\n \n{\n\n \ntype\n:\n \nrsa\n,\n\n \nalgorithm\n:\n \nrs256\n,\n\n \nkey\n:\n \nyourkeyhere\n\n \n}\n\n\n}", - "title": "Overview" - }, - { - "location": "/jwt/overview/#jwt-overview", - "text": "This guide gives an overview of using the JWT provider package.", - "title": "JWT Overview" - }, - { - "location": "/jwt/overview/#configuration", - "text": "JWTProvider can be configured in 3 different ways: Custom signers defined in jwt.json Supports (private/public): hmac , rsa , esdca . Legacy custom signer defined in jwt.json . Supports (private/public): hmac , rsa , esdca . Remote JSON Web Key Set ( jwks.json ) URL Supports (private/public): rsa . If your Vapor app is acting as an Authentication Provider, you may want to use either the Legacy custom signer setup, or the Custom signers setup, which is great if you want to perform certificates rotation. The only difference is that with Custom signers the kid value in the JWT header is not ignored, and it must match an associated signer in order to verify the signature. If your Vapor app is a Resource Provider that delegates Authentication to a 3rd party (auth0, stormpath, etc), you may want to use the Remote JSON Web Key Set setup. In this configuration the JWT token is generated by a 3rd party that provides the public key in JSON Web Key Set format.\nThe Vapor app is only in charge to verify the JWT signature using the key set provided by the 3rd party.", - "title": "Configuration" - }, - { - "location": "/jwt/overview/#remote-json-web-key-set", - "text": "Config/jwt.json { \n jwks-url : http://my-domain.com/well-known/jwks.json }", - "title": "Remote JSON Web Key Set" - }, - { - "location": "/jwt/overview/#custom-signers", - "text": "This allows to specify an array of signers and is particularly useful for rotating certificates.\nCustom signers are not backward compatible and must specify an additional kid in the configuration. type: unsigned , hmac , rsa , esdca kid: an unique identifier algorithm: type[ hmac ]: hs256 , hs384 , hs512 type[ rsa ]: rs256 , rs384 , rs512 type[ esdca ]: es256 , es384 , es512 Config/jwt.json { \n signers : { \n 1234 : { \n type : rsa , \n algorithm : rs256 , \n key : yourkeyhere \n } \n } }", - "title": "Custom Signers" - }, - { - "location": "/jwt/overview/#legacy-custom-signer", - "text": "This is backwards compatible with the previous implementation. type: unsigned , hmac , rsa , esdca algorithm: type[ hmac ]: hs256 , hs384 , hs512 type[ rsa ]: rs256 , rs384 , rs512 type[ esdca ]: es256 , es384 , es512 Config/jwt.json { \n signer : { \n type : rsa , \n algorithm : rs256 , \n key : yourkeyhere \n } }", - "title": "Legacy Custom Signer" - }, - { - "location": "/sessions/package/", - "text": "Using Sessions\n\n\nThis module is a part of Vapor, just add:\n\n\nimport\n \nSessions", - "title": "Package" - }, - { - "location": "/sessions/package/#using-sessions", - "text": "This module is a part of Vapor, just add: import Sessions", - "title": "Using Sessions" - }, - { - "location": "/sessions/sessions/", - "text": "Sessions\n\n\nSessions help you store information about a user between requests. As long as the client supports cookies, sessions are easy to create.\n\n\nMiddleware\n\n\nEnable sessions on your \nDroplet\n by adding \n\"sessions\"\n to your middleware array.\n\n\nConfig/droplet.json\n\n\n{\n\n \n...,\n\n \nmiddleware\n:\n \n[\n\n \n...\n,\n\n \nsessions\n,\n\n \n...\n,\n\n \n],\n\n \n...,\n\n\n}\n\n\n\n\n\n\nBy default, the memory sessions driver will be used. You can change this with the \ndroplet.sessions\n key.\n\n\nConfig/droplet.json\n\n\n{\n\n \n...,\n\n \nsessions\n:\n \nmemory\n,\n\n \n...,\n\n\n}\n\n\n\n\n\n\nRequest\n\n\nAfter \nSessionMiddleware\n has been enabled, you can access the \nreq.assertSession()\n method to get access to session.\n\n\nimport\n \nSessions\n\n\n\nlet\n \nsession\n \n=\n \ntry\n \nreq\n.\nassertSession\n()\n\n\nprint\n(\nsession\n.\ndata\n)\n\n\n\n\n\n\nExample\n\n\nLet's create an example that remembers the user's name.\n\n\nStore\n\n\ndrop\n.\npost\n(\nremember\n)\n \n{\n \nreq\n \nin\n\n \nguard\n \nlet\n \nname\n \n=\n \nreq\n.\ndata\n[\nname\n]?.\nstring\n \nelse\n \n{\n\n \nthrow\n \nAbort\n(.\nbadRequest\n)\n\n \n}\n\n\n \nlet\n \nsession\n \n=\n \ntry\n \nreq\n.\nassertSession\n()\n\n \ntry\n \nsession\n.\ndata\n.\nset\n(\nname\n,\n \nname\n)\n\n\n \nreturn\n \nRemebered name.\n\n\n}\n\n\n\n\n\n\nOn \nPOST /remember\n, fetch a \nname\n from the request input, then store this name into the session data.\n\n\nFetch\n\n\nOn \nGET /remember\n, fetch the \nname\n from the session data and return it.\n\n\ndrop\n.\nget\n(\nremember\n)\n \n{\n \nreq\n \nin\n\n \nlet\n \nsession\n \n=\n \ntry\n \nreq\n.\nassertSession\n()\n\n\n \nguard\n \nlet\n \nname\n \n=\n \nsession\n.\ndata\n[\nname\n]?.\nstring\n \nelse\n \n{\n\n \nreturn\n \nthrow\n \nAbort\n(.\nbadRequest\n,\n \nreason\n:\n \nPlease POST the name first.\n)\n\n \n}\n\n\n \nreturn\n \nname\n\n\n}\n\n\n\n\n\n\nCookie\n\n\nThe session will be stored using the \nvapor-session\n cookie.", - "title": "Sessions" - }, - { - "location": "/sessions/sessions/#sessions", - "text": "Sessions help you store information about a user between requests. As long as the client supports cookies, sessions are easy to create.", - "title": "Sessions" - }, - { - "location": "/sessions/sessions/#middleware", - "text": "Enable sessions on your Droplet by adding \"sessions\" to your middleware array. Config/droplet.json { \n ..., \n middleware : [ \n ... , \n sessions , \n ... , \n ], \n ..., } By default, the memory sessions driver will be used. You can change this with the droplet.sessions key. Config/droplet.json { \n ..., \n sessions : memory , \n ..., }", - "title": "Middleware" - }, - { - "location": "/sessions/sessions/#request", - "text": "After SessionMiddleware has been enabled, you can access the req.assertSession() method to get access to session. import Sessions let session = try req . assertSession () print ( session . data )", - "title": "Request" - }, - { - "location": "/sessions/sessions/#example", - "text": "Let's create an example that remembers the user's name.", - "title": "Example" - }, - { - "location": "/sessions/sessions/#store", - "text": "drop . post ( remember ) { req in \n guard let name = req . data [ name ]?. string else { \n throw Abort (. badRequest ) \n } \n\n let session = try req . assertSession () \n try session . data . set ( name , name ) \n\n return Remebered name. } On POST /remember , fetch a name from the request input, then store this name into the session data.", - "title": "Store" - }, - { - "location": "/sessions/sessions/#fetch", - "text": "On GET /remember , fetch the name from the session data and return it. drop . get ( remember ) { req in \n let session = try req . assertSession () \n\n guard let name = session . data [ name ]?. string else { \n return throw Abort (. badRequest , reason : Please POST the name first. ) \n } \n\n return name }", - "title": "Fetch" - }, - { - "location": "/sessions/sessions/#cookie", - "text": "The session will be stored using the vapor-session cookie.", - "title": "Cookie" - }, - { - "location": "/http/package/", - "text": "Using HTTP\n\n\nWith Vapor\n\n\nThis package is included with Vapor by default, just add:\n\n\nimport\n \nHTTP\n\n\n\n\n\n\nWithout Vapor\n\n\nHTTP provides everything you need to create an HTTP-based application for any server-side Swift project. To include it in your package, add the following to your \nPackage.swift\n file.\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n...\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/engine.git\n,\n \nmajorVersion\n:\n \n2\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport HTTP\n to access HTTP's APIs", - "title": "Package" - }, - { - "location": "/http/package/#using-http", - "text": "", - "title": "Using HTTP" - }, - { - "location": "/http/package/#with-vapor", - "text": "This package is included with Vapor by default, just add: import HTTP", - "title": "With Vapor" - }, - { - "location": "/http/package/#without-vapor", - "text": "HTTP provides everything you need to create an HTTP-based application for any server-side Swift project. To include it in your package, add the following to your Package.swift file. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . Package ( url : https://github.com/vapor/engine.git , majorVersion : 2 ) \n ], \n exclude : [ ... ] ) Use import HTTP to access HTTP's APIs", - "title": "Without Vapor" - }, - { - "location": "/http/request/", - "text": "Request\n\n\nThe most common part of the \nHTTP\n library we'll be interacting with is the \nRequest\n type. Here's a look at some of the most commonly used attributes in this type.\n\n\npublic\n \nvar\n \nmethod\n:\n \nMethod\n\n\npublic\n \nvar\n \nuri\n:\n \nURI\n\n\npublic\n \nvar\n \nparameters\n:\n \nNode\n\n\npublic\n \nvar\n \nheaders\n:\n \n[\nHeaderKey\n:\n \nString\n]\n\n\npublic\n \nvar\n \nbody\n:\n \nBody\n\n\npublic\n \nvar\n \ndata\n:\n \nContent\n\n\n\n\n\n\nMethod\n\n\nThe HTTP \nMethod\n associated with the \nRequest\n, ie: \nGET\n, \nPOST\n, \nPUT\n, \nPATCH\n, \nDELETE\n.\n\n\nURI\n\n\nThe associated \nURI\n of the request. We will use this to access attributes about the \nuri\n the request was sent to.\n\n\nFor example, given the following uri: \nhttp://vapor.codes/example?query=hi#fragments-too\n\n\nlet\n \nscheme\n \n=\n \nrequest\n.\nuri\n.\nscheme\n \n// http\n\n\nlet\n \nhost\n \n=\n \nrequest\n.\nuri\n.\nhost\n \n// vapor.codes\n\n\n\nlet\n \npath\n \n=\n \nrequest\n.\nuri\n.\npath\n \n// /example\n\n\nlet\n \nquery\n \n=\n \nrequest\n.\nuri\n.\nquery\n \n// query=hi\n\n\nlet\n \nfragment\n \n=\n \nrequest\n.\nuri\n.\nfragment\n \n// fragments-too\n\n\n\n\n\n\nRoute Parameters\n\n\nThe url parameters associated with the request. For example, if we have a path registered as \nhello/:name/age/:age\n, we would be able to access those in our request, like so:\n\n\nlet\n \nname\n \n=\n \nrequest\n.\nparameters\n[\nname\n]\n \n// String?\n\n\nlet\n \nage\n \n=\n \nrequest\n.\nparameters\n[\nage\n]?.\nint\n \n// Int?\n\n\n\n\n\n\nOr, to automatically throw on \nnil\n or invalid variable, you can also \nextract\n\n\nlet\n \nname\n \n=\n \ntry\n \nrequest\n.\nparameters\n.\nextract\n(\nname\n)\n \nas\n \nString\n\n\nlet\n \nage\n \n=\n \ntry\n \nrequest\n.\nparameters\n.\nextract\n(\nage\n)\n \nas\n \nInt\n\n\n\n\n\n\nThese extract functions can cast to any \nNodeInitializable\n type, including your own custom types. Make sure to check out \nNode\n for more info.\n\n\n\n\nNote: Vapor also provides type safe routing in the routing section of our docs.\n\n\n\n\nHeaders\n\n\nThese are the headers associated with the request. If you are preparing an outgoing request, this can be used to add your own keys.\n\n\nlet\n \ncontentType\n \n=\n \nrequest\n.\nheaders\n[\nContent-Type\n]\n \n\n\n\n\n\nOr for outgoing requests:\n\n\nlet\n \nrequest\n \n=\n \nRequest\n \n...\n\n\nrequest\n.\nheaders\n[\nContent-Type\n]\n \n=\n \napplication/json\n\n\nrequest\n.\nheaders\n[\nAuthorization\n]\n \n=\n \n...\n \nmy\n \nauth\n \ntoken\n\n\n\n\n\n\nExtending Headers\n\n\nWe generally seek to improve code bases by removing stringly typed code where possible. We can add variables to the headers using generic extensions.\n\n\nextension\n \nHTTP\n.\nKeyAccessible\n \nwhere\n \nKey\n \n==\n \nHeaderKey\n,\n \nValue\n \n==\n \nString\n \n{\n\n \nvar\n \ncustomKey\n:\n \nString\n?\n \n{\n\n \nget\n \n{\n\n \nreturn\n \nself\n[\nCustom-Key\n]\n\n \n}\n\n \nset\n \n{\n\n \nself\n[\nCustom-Key\n]\n \n=\n \nnewValue\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nWith this pattern implemented, our string \n\"Custom-Key\"\n is contained in one section of our code. We can now access like this:\n\n\nlet\n \ncustomKey\n \n=\n \nrequest\n.\nheaders\n.\ncustomKey\n\n\n\n// or\n\n\n\nlet\n \nrequest\n \n=\n \n...\n\n\nrequest\n.\nheaders\n.\ncustomKey\n \n=\n \nmy custom value\n\n\n\n\n\n\nBody\n\n\nThis is the body associated with the request and represents the general data payload. You can view more about body in the associated \ndocs\n\n\nFor incoming requests, we'll often pull out the associated bytes like so:\n\n\nlet\n \nrawBytes\n \n=\n \nrequest\n.\nbody\n.\nbytes\n\n\n\n\n\n\nContent\n\n\nGenerally when we're sending or receiving requests, we're using them as a way to transport content. For this, Vapor provides a convenient \ndata\n variable associated with the request that prioritizes content in a consistent way.\n\n\nFor example, say I receive a request to \nhttp://vapor.codes?hello=world\n.\n\n\nlet\n \nworld\n \n=\n \nrequest\n.\ndata\n[\nhello\n]?.\nstring\n\n\n\n\n\n\nThis same code will work if I receive a JSON request, for example:\n\n\n{\n\n \nhello\n:\n \nworld\n\n\n}\n\n\n\n\n\n\nWill still be accessible through data.\n\n\nlet\n \nworld\n \n=\n \nrequest\n.\ndata\n[\nhello\n]?.\nstring\n\n\n\n\n\n\n\n\nNote: Force unwrap should never be used.\n\n\n\n\nThis also applies to multi-part requests and can even be extended to new types such as XML or YAML through middleware.\n\n\nIf you'd prefer to access given types more explicitly, that's totally fine. The \ndata\n variable is purely opt-in convenience for those who want it.\n\n\nForm Data\n\n\nIt is common in many applications to receive forms submitted from a Web browser. Vapor provides support for several common encodings:\n\n\n// Node? from application/x-www-form-urlencoded\n\n\nlet\n \nformData\n \n=\n \nrequest\n.\nformURLEncoded\n\n\n\n// [String:Field]? from multipart/form-data\n\n\nlet\n \nmultipartFormData\n \n=\n \nrequest\n.\nformData\n\n\n\n// [Part]? from multipart/mixed\n\n\nlet\n \nmultipartMixedData\n \n=\n \nrequest\n.\nmultipart\n\n\n\n\n\n\nThese accessors will return \nnil\n if the request's \nContent-Type\n does not match what they expect.\n\n\nJSON\n\n\nTo access JSON directly on a given request, use the following:\n\n\nlet\n \njson\n \n=\n \nrequest\n.\njson\n[\nhello\n]\n\n\n\n\n\n\nQuery Parameters\n\n\nThe same applies to query convenience:\n\n\nlet\n \nquery\n \n=\n \nrequest\n.\nquery\n?[\nhello\n]\n \n// String?\n\n\nlet\n \nname\n \n=\n \nrequest\n.\nquery\n?[\nname\n]?.\nstring\n \n// String?\n\n\nlet\n \nage\n \n=\n \nrequest\n.\nquery\n?[\nage\n]?.\nint\n \n// Int?\n\n\nlet\n \nrating\n \n=\n \nrequest\n.\nquery\n?[\nrating\n]?.\ndouble\n \n// Double?\n\n\n\n\n\n\nKey Paths\n\n\nKey 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:\n\n\n{\n\n \nmetadata\n:\n \nsome metadata\n,\n\n \nartists\n \n:\n \n{\n\n \nhref\n:\n \nhttp://someurl.com\n,\n\n \nitems\n:\n \n[\n\n \n{\n\n \nname\n:\n \nVan Gogh\n,\n\n \n},\n\n \n{\n\n \nname\n:\n \nMozart\n\n \n}\n\n \n]\n\n \n}\n\n\n}\n\n\n\n\n\n\nWe could access the data in the following ways:\n\n\nMetadata\n\n\nAccess top level values\n\n\nlet\n \ntype\n \n=\n \nrequest\n.\ndata\n[\nmetadata\n].\nstring\n \n// \nsome metadata\n\n\n\n\n\n\nItems\n\n\nAccess nested values\n\n\nlet\n \nitems\n \n=\n \nrequest\n.\ndata\n[\nartists\n,\n \nitems\n]\n \n// [[\nname\n: \nVan Gogh\n], [\nname\n: \nMozart\n]]\n\n\n\n\n\n\nMixing Arrays and Objects\n\n\nGet first artists\n\n\nlet\n \nfirst\n \n=\n \nrequest\n.\ndata\n[\nartists\n,\n \nitems\n,\n \n0\n]\n \n// [\nname\n: \nVan Gogh\n]\n\n\n\n\n\n\nArray Item\n\n\nGet key from array item\n\n\nlet\n \nfirstName\n \n=\n \nrequest\n.\ndata\n[\nartists\n,\n \nitems\n,\n \n0\n,\n \nname\n]\n \n// \nVan Gogh\n\n\n\n\n\n\nArray Comprehension\n\n\nWe 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\n\n\nlet\n \nnames\n \n=\n \nrequest\n.\ndata\n[\nartists\n,\n \nitems\n,\n \nname\n]\n \n// [\nVan Gogh\n, \nMozart\n]", - "title": "Request" - }, - { - "location": "/http/request/#request", - "text": "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. 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", - "title": "Request" - }, - { - "location": "/http/request/#method", - "text": "The HTTP Method associated with the Request , ie: GET , POST , PUT , PATCH , DELETE .", - "title": "Method" - }, - { - "location": "/http/request/#uri", - "text": "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 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", - "title": "URI" - }, - { - "location": "/http/request/#route-parameters", - "text": "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: 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 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 for more info. Note: Vapor also provides type safe routing in the routing section of our docs.", - "title": "Route Parameters" - }, - { - "location": "/http/request/#headers", - "text": "These are the headers associated with the request. If you are preparing an outgoing request, this can be used to add your own keys. let contentType = request . headers [ Content-Type ] Or for outgoing requests: let request = Request ... request . headers [ Content-Type ] = application/json request . headers [ Authorization ] = ... my auth token", - "title": "Headers" - }, - { - "location": "/http/request/#extending-headers", - "text": "We generally seek to improve code bases by removing stringly typed code where possible. We can add variables to the headers using generic extensions. extension HTTP . KeyAccessible where Key == HeaderKey , Value == String { \n var customKey : String ? { \n get { \n return self [ Custom-Key ] \n } \n set { \n self [ Custom-Key ] = newValue \n } \n } } With this pattern implemented, our string \"Custom-Key\" is contained in one section of our code. We can now access like this: let customKey = request . headers . customKey // or let request = ... request . headers . customKey = my custom value", - "title": "Extending Headers" - }, - { - "location": "/http/request/#body", - "text": "This is the body associated with the request and represents the general data payload. You can view more about body in the associated docs For incoming requests, we'll often pull out the associated bytes like so: let rawBytes = request . body . bytes", - "title": "Body" - }, - { - "location": "/http/request/#content", - "text": "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 . let world = request . data [ hello ]?. string This same code will work if I receive a JSON request, for example: { \n hello : world } Will still be accessible through data. 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.", - "title": "Content" - }, - { - "location": "/http/request/#form-data", - "text": "It is common in many applications to receive forms submitted from a Web browser. Vapor provides support for several common encodings: // Node? from application/x-www-form-urlencoded let formData = request . formURLEncoded // [String:Field]? from multipart/form-data let multipartFormData = request . formData // [Part]? from multipart/mixed let multipartMixedData = request . multipart These accessors will return nil if the request's Content-Type does not match what they expect.", - "title": "Form Data" - }, - { - "location": "/http/request/#json", - "text": "To access JSON directly on a given request, use the following: let json = request . json [ hello ]", - "title": "JSON" - }, - { - "location": "/http/request/#query-parameters", - "text": "The same applies to query convenience: 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?", - "title": "Query Parameters" - }, - { - "location": "/http/request/#key-paths", - "text": "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: { \n metadata : some metadata , \n artists : { \n href : http://someurl.com , \n items : [ \n { \n name : Van Gogh , \n }, \n { \n name : Mozart \n } \n ] \n } } We could access the data in the following ways:", - "title": "Key Paths" - }, - { - "location": "/http/request/#metadata", - "text": "Access top level values let type = request . data [ metadata ]. string // some metadata", - "title": "Metadata" - }, - { - "location": "/http/request/#items", - "text": "Access nested values let items = request . data [ artists , items ] // [[ name : Van Gogh ], [ name : Mozart ]]", - "title": "Items" - }, - { - "location": "/http/request/#mixing-arrays-and-objects", - "text": "Get first artists let first = request . data [ artists , items , 0 ] // [ name : Van Gogh ]", - "title": "Mixing Arrays and Objects" - }, - { - "location": "/http/request/#array-item", - "text": "Get key from array item let firstName = request . data [ artists , items , 0 , name ] // Van Gogh", - "title": "Array Item" - }, - { - "location": "/http/request/#array-comprehension", - "text": "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 let names = request . data [ artists , items , name ] // [ Van Gogh , Mozart ]", - "title": "Array Comprehension" - }, - { - "location": "/http/response/", - "text": "Response\n\n\nWhen building endpoints, we'll often be returning responses for requests. If we're making outgoing requests, we'll be receiving them.\n\n\npublic\n \nlet\n \nstatus\n:\n \nStatus\n\n\npublic\n \nvar\n \nheaders\n:\n \n[\nHeaderKey\n:\n \nString\n]\n\n\npublic\n \nvar\n \nbody\n:\n \nBody\n\n\npublic\n \nvar\n \ndata\n:\n \nContent\n\n\n\n\n\n\nStatus\n\n\nThe http status associated with the event, for example \n.ok\n == 200 ok.\n\n\nHeaders\n\n\nThese are the headers associated with the request. If you are preparing an outgoing response, this can be used to add your own keys.\n\n\nlet\n \ncontentType\n \n=\n \nresponse\n.\nheaders\n[\nContent-Type\n]\n \n\n\n\n\n\nOr for outgoing response:\n\n\nlet\n \nresponse\n \n=\n \nresponse\n \n...\n\n\nresponse\n.\nheaders\n[\nContent-Type\n]\n \n=\n \napplication/json\n\n\nresponse\n.\nheaders\n[\nAuthorization\n]\n \n=\n \n...\n \nmy\n \nauth\n \ntoken\n\n\n\n\n\n\nExtending Headers\n\n\nWe generally seek to improve code bases by removing stringly typed code where possible. We can add variables to the headers using generic extensions.\n\n\nextension\n \nHTTP\n.\nKeyAccessible\n \nwhere\n \nKey\n \n==\n \nHeaderKey\n,\n \nValue\n \n==\n \nString\n \n{\n\n \nvar\n \ncustomKey\n:\n \nString\n?\n \n{\n\n \nget\n \n{\n\n \nreturn\n \nself\n[\nCustom-Key\n]\n\n \n}\n\n \nset\n \n{\n\n \nself\n[\nCustom-Key\n]\n \n=\n \nnewValue\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nWith this pattern implemented, our string \n\"Custom-Key\"\n is contained in one section of our code. We can now access like this:\n\n\nlet\n \ncustomKey\n \n=\n \nresponse\n.\nheaders\n.\ncustomKey\n\n\n\n// or\n\n\n\nlet\n \nrequest\n \n=\n \n...\n\n\nresponse\n.\nheaders\n.\ncustomKey\n \n=\n \nmy custom value\n\n\n\n\n\n\nBody\n\n\nThis is the body associated with the response and represents the general data payload. You can view more about body in the associated \ndocs\n\n\nFor responses, the body is most commonly set at initialization. With two main types.\n\n\nBodyRepresentable\n\n\nThings that can be converted to bytes, ie:\n\n\nlet\n \nresponse\n \n=\n \nResponse\n(\nstatus\n:\n \n.\nok\n,\n \nbody\n:\n \nsome string\n)\n\n\n\n\n\n\nIn the above example, the \nString\n will be automatically converted to a body. Your own types can do this as well.\n\n\nBytes Directly\n\n\nIf we already have our bytes array, we can pass it into the body like so:\n\n\nlet\n \nresponse\n \n=\n \nResponse\n(\nstatus\n:\n \n.\nok\n,\n \nbody\n:\n \n.\ndata\n(\nmyArrayOfBytes\n))\n\n\n\n\n\n\nChunked\n\n\nTo send an \nHTTP.Response\n in chunks, we can pass a closure that we'll use to send our response body in parts.\n\n\nlet\n \nresponse\n \n=\n \nResponse\n(\nstatus\n:\n \n.\nok\n)\n \n{\n \nchunker\n \nin\n\n \nfor\n \nname\n \nin\n \n[\njoe\n,\n \npam\n,\n \ncheryl\n]\n \n{\n\n \nsleep\n(\n1\n)\n\n \ntry\n \nchunker\n.\nsend\n(\nname\n)\n\n \n}\n\n\n \ntry\n \nchunker\n.\nclose\n()\n\n\n}\n\n\n\n\n\n\n\n\nMake sure to call \nclose()\n before the chunker leaves scope.\n\n\n\n\nContent\n\n\nWe can access content the same we do in a \nrequest\n. This most commonly applies to outgoing requests.\n\n\nlet\n \npokemonResponse\n \n=\n \ntry\n \ndrop\n.\nclient\n.\nget\n(\nhttp://pokeapi.co/api/v2/pokemon/\n)\n\n\nlet\n \nnames\n \n=\n \npokemonResponse\n.\ndata\n[\nresults\n,\n \nname\n]?.\narray\n\n\n\n\n\n\nJSON\n\n\nTo access JSON directly on a given response, use the following:\n\n\nlet\n \njson\n \n=\n \nrequest\n.\nresponse\n[\nhello\n]\n\n\n\n\n\n\nKey Paths\n\n\nFor more on KeyPaths, visit \nhere\n\n\nServing Files\n\n\nIf you are simply looking to serve files from your public directory,\nit may be useful to look at 'FileMiddleware' instead.\n\n\nlet\n \nres\n \n=\n \ntry\n \nResponse\n(\nfilePath\n:\n \n/path/to/file.txt\n)\n\n\n\n\n\n\nUse this to initialize a file response for the exact file path.\nIf using from a public folder for example, the file name should be appended\nto the public directory, ie: \ndrop.publicDir + \"myFile.cool\"\n\n\nResponse\n(\nfilePath\n:\n \nString\n,\n \nifNoneMatch\n:\n \nString\n?\n \n=\n \nnil\n,\n \nchunkSize\n:\n \nInt\n \n=\n \n2048\n)\n \nthrows\n\n\n\n\n\n\nIf none match represents an ETag that will be used to check if the file has\nchanged since the last load by the client. This allows clients like browsers\nto cache their files and avoid downloading resources unnecessarily.\nMost often calculated w/ https://tools.ietf.org/html/rfc7232#section-3.2\n\n\nFor an example of how this is used, look at 'FileMiddleware'.", - "title": "Response" - }, - { - "location": "/http/response/#response", - "text": "When building endpoints, we'll often be returning responses for requests. If we're making outgoing requests, we'll be receiving them. public let status : Status public var headers : [ HeaderKey : String ] public var body : Body public var data : Content", - "title": "Response" - }, - { - "location": "/http/response/#status", - "text": "The http status associated with the event, for example .ok == 200 ok.", - "title": "Status" - }, - { - "location": "/http/response/#headers", - "text": "These are the headers associated with the request. If you are preparing an outgoing response, this can be used to add your own keys. let contentType = response . headers [ Content-Type ] Or for outgoing response: let response = response ... response . headers [ Content-Type ] = application/json response . headers [ Authorization ] = ... my auth token", - "title": "Headers" - }, - { - "location": "/http/response/#extending-headers", - "text": "We generally seek to improve code bases by removing stringly typed code where possible. We can add variables to the headers using generic extensions. extension HTTP . KeyAccessible where Key == HeaderKey , Value == String { \n var customKey : String ? { \n get { \n return self [ Custom-Key ] \n } \n set { \n self [ Custom-Key ] = newValue \n } \n } } With this pattern implemented, our string \"Custom-Key\" is contained in one section of our code. We can now access like this: let customKey = response . headers . customKey // or let request = ... response . headers . customKey = my custom value", - "title": "Extending Headers" - }, - { - "location": "/http/response/#body", - "text": "This is the body associated with the response and represents the general data payload. You can view more about body in the associated docs For responses, the body is most commonly set at initialization. With two main types.", - "title": "Body" - }, - { - "location": "/http/response/#bodyrepresentable", - "text": "Things that can be converted to bytes, ie: 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.", - "title": "BodyRepresentable" - }, - { - "location": "/http/response/#bytes-directly", - "text": "If we already have our bytes array, we can pass it into the body like so: let response = Response ( status : . ok , body : . data ( myArrayOfBytes ))", - "title": "Bytes Directly" - }, - { - "location": "/http/response/#chunked", - "text": "To send an HTTP.Response in chunks, we can pass a closure that we'll use to send our response body in parts. let response = Response ( status : . ok ) { chunker in \n for name in [ joe , pam , cheryl ] { \n sleep ( 1 ) \n try chunker . send ( name ) \n } \n\n try chunker . close () } Make sure to call close() before the chunker leaves scope.", - "title": "Chunked" - }, - { - "location": "/http/response/#content", - "text": "We can access content the same we do in a request . This most commonly applies to outgoing requests. let pokemonResponse = try drop . client . get ( http://pokeapi.co/api/v2/pokemon/ ) let names = pokemonResponse . data [ results , name ]?. array", - "title": "Content" - }, - { - "location": "/http/response/#json", - "text": "To access JSON directly on a given response, use the following: let json = request . response [ hello ]", - "title": "JSON" - }, - { - "location": "/http/response/#key-paths", - "text": "For more on KeyPaths, visit here", - "title": "Key Paths" - }, - { - "location": "/http/response/#serving-files", - "text": "If you are simply looking to serve files from your public directory,\nit may be useful to look at 'FileMiddleware' instead. let res = try Response ( filePath : /path/to/file.txt ) Use this to initialize a file response for the exact file path.\nIf using from a public folder for example, the file name should be appended\nto the public directory, ie: drop.publicDir + \"myFile.cool\" Response ( filePath : String , ifNoneMatch : String ? = nil , chunkSize : Int = 2048 ) throws If none match represents an ETag that will be used to check if the file has\nchanged since the last load by the client. This allows clients like browsers\nto cache their files and avoid downloading resources unnecessarily.\nMost often calculated w/ https://tools.ietf.org/html/rfc7232#section-3.2 For an example of how this is used, look at 'FileMiddleware'.", - "title": "Serving Files" - }, - { - "location": "/http/middleware/", - "text": "Middleware\n\n\nMiddleware 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.\n\n\nYou can imagine middleware as a chain of logic connecting your server to the client requesting your web app.\n\n\nVersion Middleware\n\n\nAs 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:\n\n\nimport\n \nHTTP\n\n\n\nfinal\n \nclass\n \nVersionMiddleware\n:\n \nMiddleware\n \n{\n\n \nfunc\n \nrespond\n(\nto\n \nrequest\n:\n \nRequest\n,\n \nchainingTo\n \nnext\n:\n \nResponder\n)\n \nthrows\n \n-\n \nResponse\n \n{\n\n \nlet\n \nresponse\n \n=\n \ntry\n \nnext\n.\nrespond\n(\nto\n:\n \nrequest\n)\n\n\n \nresponse\n.\nheaders\n[\nVersion\n]\n \n=\n \nAPI v1.0\n\n\n \nreturn\n \nresponse\n\n \n}\n\n\n}\n\n\n\n\n\n\nWe then supply this middleware to our \nDroplet\n.\n\n\nimport\n \nVapor\n\n\n\nlet\n \nconfig\n \n=\n \ntry\n \nConfig\n()\n\n\n\nconfig\n.\naddConfigurable\n(\nmiddleware\n:\n \nVersionMiddleware\n(),\n \nname\n:\n \nversion\n)\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nconfig\n)\n\n\n\n\n\n\n\n\nTip\n\n\nYou can now dynamically enable and disable this middleware from your configuration files. \nSimply add \n\"version\"\n to the \n\"middleware\"\n array in your \ndroplet.json\n file.\nSee the \nconfiguration\n section for more information.\n\n\n\n\nYou can imagine our \nVersionMiddleware\n 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.\n\n\n\n\nBreakdown\n\n\nLet's break down the middleware line by line.\n\n\nlet\n \nresponse\n \n=\n \ntry\n \nnext\n.\nrespond\n(\nto\n:\n \nrequest\n)\n\n\n\n\n\n\nSince the \nVersionMiddleware\n 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 \nDroplet\n and comes back with the response that should be sent to the client.\n\n\nresponse\n.\nheaders\n[\nVersion\n]\n \n=\n \nAPI v1.0\n\n\n\n\n\n\nWe then \nmodify\n the response to contain a Version header.\n\n\nreturn\n \nresponse\n\n\n\n\n\n\nThe response is returned and will chain back up any remaining middleware and back to the client.\n\n\nRequest\n\n\nThe middleware can also modify or interact with the request.\n\n\nfunc\n \nrespond\n(\nto\n \nrequest\n:\n \nRequest\n,\n \nchainingTo\n \nnext\n:\n \nResponder\n)\n \nthrows\n \n-\n \nResponse\n \n{\n\n \nguard\n \nrequest\n.\ncookies\n[\ntoken\n]\n \n==\n \nsecret\n \nelse\n \n{\n\n \nthrow\n \nAbort\n(.\nbadRequest\n)\n\n \n}\n\n\n \nreturn\n \ntry\n \nnext\n.\nrespond\n(\nto\n:\n \nrequest\n)\n\n\n}\n\n\n\n\n\n\nThis middleware will require that the request has a cookie named \ntoken\n that equals \nsecret\n or else the request will be aborted.\n\n\nErrors\n\n\nMiddleware 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:\n\n\nenum\n \nFooError\n:\n \nError\n \n{\n\n \ncase\n \nfooServiceUnavailable\n\n\n}\n\n\n\n\n\n\nSay there is a custom error that either you defined or one of the APIs you are using \nthrows\n. This error must be caught when thrown, or else it will end up as an internal server error (500) which may be unexpected to a user. The most obvious solution is to catch the error in the route closure.\n\n\napp\n.\nget\n(\nfoo\n)\n \n{\n \nrequest\n \nin\n\n \nlet\n \nfoo\n:\n \nFoo\n\n \ndo\n \n{\n\n \nfoo\n \n=\n \ntry\n \ngetFooFromService\n()\n\n \n}\n \ncatch\n \n{\n\n \nthrow\n \nAbort\n(.\nbadRequest\n)\n\n \n}\n\n\n \n// continue with Foo object\n\n\n}\n\n\n\n\n\n\nThis solution works, but it would get repetitive if multiple routes need to handle the error. Luckily, this error could be caught in a middleware instead.\n\n\nfinal\n \nclass\n \nFooErrorMiddleware\n:\n \nMiddleware\n \n{\n\n \nfunc\n \nrespond\n(\nto\n \nrequest\n:\n \nRequest\n,\n \nchainingTo\n \nnext\n:\n \nResponder\n)\n \nthrows\n \n-\n \nResponse\n \n{\n\n \ndo\n \n{\n\n \nreturn\n \ntry\n \nnext\n.\nrespond\n(\nto\n:\n \nrequest\n)\n\n \n}\n \ncatch\n \nFooError\n.\nfooServiceUnavailable\n \n{\n\n \nthrow\n \nAbort\n(\n\n \n.\nbadRequest\n,\n\n \nreason\n:\n \nSorry, we were unable to query the Foo service.\n\n \n)\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nWe just need to add this middleware to our droplet's config.\n\n\nconfig\n.\naddConfigurable\n(\nmiddleware\n:\n \nFooErrorMiddleware\n(),\n \nname\n:\n \nfoo-error\n)\n\n\n\n\n\n\n\n\nTip\n\n\nDon't forget to enable the middleware in your \ndroplet.json\n file.\n\n\n\n\nNow our route closures look a lot better and we don't have to worry about code duplication.\n\n\napp\n.\nget\n(\nfoo\n)\n \n{\n \nrequest\n \nin\n\n \nlet\n \nfoo\n \n=\n \ntry\n \ngetFooFromService\n()\n\n\n \n// continue with Foo object\n\n\n}\n\n\n\n\n\n\nRoute Groups\n\n\nFor more granularity, Middleware can be applied to specific route groups.\n\n\nlet\n \nauthed\n \n=\n \ndrop\n.\ngrouped\n(\nAuthMiddleware\n())\n\n\nauthed\n.\nget\n(\nsecure\n)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nSecrets\n.\nall\n().\nmakeJSON\n()\n\n\n}\n\n\n\n\n\n\nAnything added to the \nauthed\n group must pass through \nAuthMiddleware\n. Because of this, we can assume all traffic to \n/secure\n has been authorized. Learn more in \nRouting\n.\n\n\nConfiguration\n\n\nYou can use the \nconfiguration\n files to enabled or disable middleware dynamically. This is especially useful if you have middleware that should, for example, run only in production.\n\n\nAppending configurable middleware looks like the following:\n\n\nlet\n \nconfig\n \n=\n \ntry\n \nConfig\n()\n\n\n\nconfig\n.\naddConfigurable\n(\nmiddleware\n:\n \nmyMiddleware\n,\n \nname\n:\n \nmy-middleware\n)\n\n\n\nlet\n \ndrop\n \n=\n \nDroplet\n(\nconfig\n)\n\n\n\n\n\n\nThen, in the \nConfig/droplet.json\n file, add \nmy-middleware\n to the \nmiddleware\n array.\n\n\n{\n\n \n...\n\n \nmiddleware\n:\n \n{\n\n \n...\n\n \nmy-middleware\n,\n\n \n...\n\n \n},\n\n \n...\n\n\n}\n\n\n\n\n\n\nIf the name of the added middleware appears in the middleware array it will be added to the server's middleware when the application boots.\n\n\nThe ordering of middleware is respected.\n\n\nManual\n\n\nYou can also hardcode your middleware if you don't want to use configuration files.\n\n\nimport\n \nVapor\n\n\n\nlet\n \nversionMiddleware\n \n=\n \nVersionMiddleware\n()\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nmiddleware\n:\n \n[\nversionMiddleware\n])\n\n\n\n\n\n\nAdvanced\n\n\nExtensions\n\n\nMiddleware pairs great with request/response extensions and storage. This example shows you how to dynamically return either HTML or JSON responses for a Model depending on the type of client.\n\n\nMiddleware\n\n\nfinal\n \nclass\n \nPokemonMiddleware\n:\n \nMiddleware\n \n{\n\n \nlet\n \nview\n:\n \nViewProtocol\n\n \ninit\n(\n_\n \nview\n:\n \nViewProtocol\n)\n \n{\n\n \nself\n.\nview\n \n=\n \nview\n\n \n}\n\n\n \nfunc\n \nrespond\n(\nto\n \nrequest\n:\n \nRequest\n,\n \nchainingTo\n \nnext\n:\n \nResponder\n)\n \nthrows\n \n-\n \nResponse\n \n{\n\n \nlet\n \nresponse\n \n=\n \ntry\n \nnext\n.\nrespond\n(\nto\n:\n \nrequest\n)\n\n\n \nif\n \nlet\n \npokemon\n \n=\n \nresponse\n.\npokemon\n \n{\n\n \nif\n \nrequest\n.\naccept\n.\nprefers\n(\nhtml\n)\n \n{\n\n \nresponse\n.\nview\n \n=\n \ntry\n \nview\n.\nmake\n(\npokemon.mustache\n,\n \npokemon\n)\n\n \n}\n \nelse\n \n{\n\n \nresponse\n.\njson\n \n=\n \ntry\n \npokemon\n.\nmakeJSON\n()\n\n \n}\n\n \n}\n\n\n \nreturn\n \nresponse\n\n \n}\n\n\n}\n\n\n\nextension\n \nPokemonMiddleware\n:\n \nConfigInitializable\n \n{\n\n \nconvenience\n \ninit\n(\nconfig\n:\n \nConfig\n)\n \nthrows\n \n{\n\n \nlet\n \nview\n \n=\n \ntry\n \nconfig\n.\nresolveView\n()\n\n \nself\n.\ninit\n(\nview\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nResponse\n\n\nAnd the extension to \nResponse\n.\n\n\nextension\n \nResponse\n \n{\n\n \nvar\n \npokemon\n:\n \nPokemon\n?\n \n{\n\n \nget\n \n{\n \nreturn\n \nstorage\n[\npokemon\n]\n \nas\n?\n \nPokemon\n \n}\n\n \nset\n \n{\n \nstorage\n[\npokemon\n]\n \n=\n \nnewValue\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nIn this example, we added a new property to response capable of holding a Pok\u00e9mon object. If the middleware finds a response with one of these Pok\u00e9mon 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.\n\n\nUsage\n\n\nYour closures can now look something like this:\n\n\nimport\n \nVapor\n\n\n\nlet\n \nconfig\n \n=\n \ntry\n \nConfig\n()\n\n\nconfig\n.\naddConfigurable\n(\nmiddleware\n:\n \nPokemonMiddleware\n.\ninit\n,\n \nname\n:\n \npokemon\n)\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nconfig\n)\n\n\n\ndrop\n.\nget\n(\npokemon\n,\n \nPokemon\n.\nself\n)\n \n{\n \nrequest\n,\n \npokemon\n \nin\n\n \nlet\n \nresponse\n \n=\n \nResponse\n()\n\n \nresponse\n.\npokemon\n \n=\n \npokemon\n\n \nreturn\n \nresponse\n\n\n}\n\n\n\n\n\n\n\n\nTip\n\n\nDon't forget to add \n\"pokemon\"\n to your \ndroplet.json\n middleware array.\n\n\n\n\nResponse Representable\n\n\nIf you want to go a step further, you can make \nPokemon\n conform to \nResponseRepresentable\n.\n\n\nimport\n \nHTTP\n\n\n\nextension\n \nPokemon\n:\n \nResponseRepresentable\n \n{\n\n \nfunc\n \nmakeResponse\n()\n \nthrows\n \n-\n \nResponse\n \n{\n\n \nlet\n \nresponse\n \n=\n \nResponse\n()\n\n \nresponse\n.\npokemon\n \n=\n \nself\n\n \nreturn\n \nresponse\n\n \n}\n\n\n}\n\n\n\n\n\n\nNow your route closures are greatly simplified.\n\n\ndrop\n.\nget\n(\npokemon\n,\n \nPokemon\n.\nself\n)\n \n{\n \nrequest\n,\n \npokemon\n \nin\n\n \nreturn\n \npokemon\n\n\n}\n\n\n\n\n\n\nMiddleware is incredibly powerful. Combined with extensions, it allows you to add functionality that feels native to the framework.", - "title": "Middleware" - }, - { - "location": "/http/middleware/#middleware", - "text": "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.", - "title": "Middleware" - }, - { - "location": "/http/middleware/#version-middleware", - "text": "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: import HTTP final class VersionMiddleware : Middleware { \n func respond ( to request : Request , chainingTo next : Responder ) throws - Response { \n let response = try next . respond ( to : request ) \n\n response . headers [ Version ] = API v1.0 \n\n return response \n } } We then supply this middleware to our Droplet . import Vapor let config = try Config () config . addConfigurable ( middleware : VersionMiddleware (), name : version ) let drop = try Droplet ( config ) Tip You can now dynamically enable and disable this middleware from your configuration files. \nSimply add \"version\" to the \"middleware\" array in your droplet.json file.\nSee the configuration section for more information. 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.", - "title": "Version Middleware" - }, - { - "location": "/http/middleware/#breakdown", - "text": "Let's break down the middleware line by line. 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. response . headers [ Version ] = API v1.0 We then modify the response to contain a Version header. return response The response is returned and will chain back up any remaining middleware and back to the client.", - "title": "Breakdown" - }, - { - "location": "/http/middleware/#request", - "text": "The middleware can also modify or interact with the request. func respond ( to request : Request , chainingTo next : Responder ) throws - Response { \n guard request . cookies [ token ] == secret else { \n throw Abort (. badRequest ) \n } \n\n 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.", - "title": "Request" - }, - { - "location": "/http/middleware/#errors", - "text": "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: enum FooError : Error { \n 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 an internal server error (500) which may be unexpected to a user. The most obvious solution is to catch the error in the route closure. app . get ( foo ) { request in \n let foo : Foo \n do { \n foo = try getFooFromService () \n } catch { \n throw Abort (. badRequest ) \n } \n\n // continue with Foo object } This solution works, but it would get repetitive if multiple routes need to handle the error. Luckily, this error could be caught in a middleware instead. final class FooErrorMiddleware : Middleware { \n func respond ( to request : Request , chainingTo next : Responder ) throws - Response { \n do { \n return try next . respond ( to : request ) \n } catch FooError . fooServiceUnavailable { \n throw Abort ( \n . badRequest , \n reason : Sorry, we were unable to query the Foo service. \n ) \n } \n } } We just need to add this middleware to our droplet's config. config . addConfigurable ( middleware : FooErrorMiddleware (), name : foo-error ) Tip Don't forget to enable the middleware in your droplet.json file. Now our route closures look a lot better and we don't have to worry about code duplication. app . get ( foo ) { request in \n let foo = try getFooFromService () \n\n // continue with Foo object }", - "title": "Errors" - }, - { - "location": "/http/middleware/#route-groups", - "text": "For more granularity, Middleware can be applied to specific route groups. let authed = drop . grouped ( AuthMiddleware ()) authed . get ( secure ) { req in \n return Secrets . all (). makeJSON () } Anything added to the authed group must pass through AuthMiddleware . Because of this, we can assume all traffic to /secure has been authorized. Learn more in Routing .", - "title": "Route Groups" - }, - { - "location": "/http/middleware/#configuration", - "text": "You can use the configuration files to enabled or disable middleware dynamically. This is especially useful if you have middleware that should, for example, run only in production. Appending configurable middleware looks like the following: let config = try Config () config . addConfigurable ( middleware : myMiddleware , name : my-middleware ) let drop = Droplet ( config ) Then, in the Config/droplet.json file, add my-middleware to the middleware array. { \n ... \n middleware : { \n ... \n my-middleware , \n ... \n }, \n ... } If the name of the added middleware appears in the middleware array it will be added to the server's middleware when the application boots. The ordering of middleware is respected.", - "title": "Configuration" - }, - { - "location": "/http/middleware/#manual", - "text": "You can also hardcode your middleware if you don't want to use configuration files. import Vapor let versionMiddleware = VersionMiddleware () let drop = try Droplet ( middleware : [ versionMiddleware ])", - "title": "Manual" - }, - { - "location": "/http/middleware/#advanced", - "text": "", - "title": "Advanced" - }, - { - "location": "/http/middleware/#extensions", - "text": "Middleware pairs great with request/response extensions and storage. This example shows you how to dynamically return either HTML or JSON responses for a Model depending on the type of client.", - "title": "Extensions" - }, - { - "location": "/http/middleware/#middleware_1", - "text": "final class PokemonMiddleware : Middleware { \n let view : ViewProtocol \n init ( _ view : ViewProtocol ) { \n self . view = view \n } \n\n func respond ( to request : Request , chainingTo next : Responder ) throws - Response { \n let response = try next . respond ( to : request ) \n\n if let pokemon = response . pokemon { \n if request . accept . prefers ( html ) { \n response . view = try view . make ( pokemon.mustache , pokemon ) \n } else { \n response . json = try pokemon . makeJSON () \n } \n } \n\n return response \n } } extension PokemonMiddleware : ConfigInitializable { \n convenience init ( config : Config ) throws { \n let view = try config . resolveView () \n self . init ( view ) \n } }", - "title": "Middleware" - }, - { - "location": "/http/middleware/#response", - "text": "And the extension to Response . extension Response { \n var pokemon : Pokemon ? { \n get { return storage [ pokemon ] as ? Pokemon } \n set { storage [ pokemon ] = newValue } \n } } In this example, we added a new property to response capable of holding a Pok\u00e9mon object. If the middleware finds a response with one of these Pok\u00e9mon 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.", - "title": "Response" - }, - { - "location": "/http/middleware/#usage", - "text": "Your closures can now look something like this: import Vapor let config = try Config () config . addConfigurable ( middleware : PokemonMiddleware . init , name : pokemon ) let drop = try Droplet ( config ) drop . get ( pokemon , Pokemon . self ) { request , pokemon in \n let response = Response () \n response . pokemon = pokemon \n return response } Tip Don't forget to add \"pokemon\" to your droplet.json middleware array.", - "title": "Usage" - }, - { - "location": "/http/middleware/#response-representable", - "text": "If you want to go a step further, you can make Pokemon conform to ResponseRepresentable . import HTTP extension Pokemon : ResponseRepresentable { \n func makeResponse () throws - Response { \n let response = Response () \n response . pokemon = self \n return response \n } } Now your route closures are greatly simplified. drop . get ( pokemon , Pokemon . self ) { request , pokemon in \n return pokemon } Middleware is incredibly powerful. Combined with extensions, it allows you to add functionality that feels native to the framework.", - "title": "Response Representable" - }, - { - "location": "/http/body/", - "text": "Body\n\n\nThe \nHTTP.Body\n represents the payload of an \nHTTP.Message\n, and is used to pass the underlying data. Some examples of this in practice would be \nJSON\n, \nHTML\n text, or the bytes of an image. Let's look at the implementation:\n\n\npublic\n \nenum\n \nBody\n \n{\n\n \ncase\n \ndata\n(\nBytes\n)\n\n \ncase\n \nchunked\n((\nChunkStream\n)\n \nthrows\n \n-\n \nVoid\n)\n\n\n}\n\n\n\n\n\n\nData Case\n\n\nThe \ndata\n case is by far the most common use for a \nBody\n in an \nHTTP.Message\n. It is simply an array of bytes. The serialization protocol or type associated with these bytes is usually defined by the \nContent-Type\n header. Let's look at some examples.\n\n\nApplication/JSON\n\n\nIf our \nContent-Type\n header contains \napplication/json\n, then the underlying bytes represent serialized JSON.\n\n\nif\n \nlet\n \ncontentType\n \n=\n \nreq\n.\nheaders\n[\nContent-Type\n],\n \ncontentType\n.\ncontains\n(\napplication/json\n),\n \nlet\n \nbytes\n \n=\n \nreq\n.\nbody\n.\nbytes\n \n{\n\n \nlet\n \njson\n \n=\n \ntry\n \nJSON\n(\nbytes\n:\n \nbytes\n)\n\n \nprint\n(\nGot JSON: \n\\(\njson\n)\n)\n\n\n}\n\n\n\n\n\n\nImage/PNG\n\n\nIf our \nContent-Type\n contains \nimage/png\n, then the underlying bytes represent an encoded png.\n\n\nif\n \nlet\n \ncontentType\n \n=\n \nreq\n.\nheaders\n[\nContent-Type\n],\n \ncontentType\n.\ncontains\n(\nimage/png\n),\n \nlet\n \nbytes\n \n=\n \nreq\n.\nbody\n.\nbytes\n \n{\n\n \ntry\n \ndatabase\n.\nsave\n(\nimage\n:\n \nbytes\n)\n\n\n}\n\n\n\n\n\n\nChunked Case\n\n\nThe \nchunked\n case only applies to outgoing \nHTTP.Message\ns 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.\n\n\nlet\n \nbody\n:\n \nBody\n \n=\n \nBody\n.\nchunked\n(\nsender\n)\n\n\nreturn\n \nResponse\n(\nstatus\n:\n \n.\nok\n,\n \nbody\n:\n \nbody\n)\n\n\n\n\n\n\nWe can implement this manually, or use Vapor's built in convenience initializer for chunked bodies:\n\n\nreturn\n \nResponse\n(\nstatus\n:\n \n.\nok\n)\n \n{\n \nchunker\n \nin\n\n \nfor\n \nname\n \nin\n \n[\njoe\n,\n \npam\n,\n \ncheryl\n]\n \n{\n\n \nsleep\n(\n1\n)\n\n \ntry\n \nchunker\n.\nsend\n(\nname\n)\n\n \n}\n\n\n \ntry\n \nchunker\n.\nclose\n()\n\n\n}\n\n\n\n\n\n\n\n\nMake sure to call \nclose()\n before the chunker leaves scope.\n\n\n\n\nBodyRepresentable\n\n\nIn addition to the concrete \nBody\n type, as is common in Vapor, we also have wide support for \nBodyRepresentable\n. This means objects that we're commonly converting to \nBody\n type can be used interchangeably. For example:\n\n\nreturn\n \nResponse\n(\nbody\n:\n \nHello, World!\n)\n\n\n\n\n\n\nIn the above example, string is converted to bytes and added to the body.\n\n\n\n\nIn practice, it is better to use \nreturn \"Hello, World!\"\n. Vapor will automatically be able to set the \nContent-Type\n to appropriate values.\n\n\n\n\nLet's look at how it's implemented:\n\n\npublic\n \nprotocol\n \nBodyRepresentable\n \n{\n\n \nfunc\n \nmakeBody\n()\n \n-\n \nBody\n\n\n}\n\n\n\n\n\n\nCustom\n\n\nWe can conform our own types to this as well where applicable. Let's pretend we have a custom data type, \n.vpr\n. Let's conform our \nVPR\n file type model:\n\n\nextension\n \nVPRFile\n:\n \nHTTP\n.\nBodyRepresentable\n \n{\n\n \nfunc\n \nmakeBody\n()\n \n-\n \nBody\n \n{\n\n \n// collect bytes\n\n \nreturn\n \n.\ndata\n(\nbytes\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\n\n\nYou 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.\n\n\n\n\nNow we're able to include our \nVPR\n file directly in our \nResponses\n.\n\n\ndrop\n.\nget\n(\nfiles\n,\n \n:file-name\n)\n \n{\n \nrequest\n \nin\n\n \nlet\n \nfilename\n \n=\n \ntry\n \nrequest\n.\nparameters\n.\nextract\n(\nfile-name\n)\n \nas\n \nString\n\n \nlet\n \nfile\n \n=\n \nVPRFileManager\n.\nfetch\n(\nfilename\n)\n\n \nreturn\n \nResponse\n(\nstatus\n:\n \n.\nok\n,\n \nheaders\n:\n \n[\nContent-Type\n:\n \nfile/vpr\n],\n \nbody\n:\n \nfile\n)\n\n\n}\n\n\n\n\n\n\nIn practice, if we're repeating this often, we'll probably conform \nVPRFile\n directly to \nResponseRepresentable\n\n\nextension\n \nVPRFile\n:\n \nHTTP\n.\nResponseRepresentable\n \n{\n\n \nfunc\n \nmakeResponse\n()\n \n-\n \nResponse\n \n{\n\n \nreturn\n \nResponse\n(\n\n \nstatus\n:\n \n.\nok\n,\n\n \nheaders\n:\n \n[\nContent-Type\n:\n \nfile/vpr\n],\n\n \nbody\n:\n \nfile\n\n \n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nHere's our above example now:\n\n\ndrop\n.\nget\n(\nfiles\n,\n \n:file-name\n)\n \n{\n \nrequest\n \nin\n\n \nlet\n \nfilename\n \n=\n \ntry\n \nrequest\n.\nparameters\n.\nextract\n(\nfile-name\n)\n \nas\n \nString\n\n \nreturn\n \nVPRFileManager\n.\nfetch\n(\nfilename\n)\n\n\n}\n\n\n\n\n\n\nWe could also use type-safe routing to make this even more concise:\n\n\ndrop\n.\nget\n(\nfiles\n,\n \nString\n.\nself\n)\n \n{\n \nrequest\n,\n \nfilename\n \nin\n\n \nreturn\n \nVPRFileManager\n.\nfetch\n(\nfilename\n)\n\n\n}", - "title": "Body" - }, - { - "location": "/http/body/#body", - "text": "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: public enum Body { \n case data ( Bytes ) \n case chunked (( ChunkStream ) throws - Void ) }", - "title": "Body" - }, - { - "location": "/http/body/#data-case", - "text": "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.", - "title": "Data Case" - }, - { - "location": "/http/body/#applicationjson", - "text": "If our Content-Type header contains application/json , then the underlying bytes represent serialized JSON. if let contentType = req . headers [ Content-Type ], contentType . contains ( application/json ), let bytes = req . body . bytes { \n let json = try JSON ( bytes : bytes ) \n print ( Got JSON: \\( json ) ) }", - "title": "Application/JSON" - }, - { - "location": "/http/body/#imagepng", - "text": "If our Content-Type contains image/png , then the underlying bytes represent an encoded png. if let contentType = req . headers [ Content-Type ], contentType . contains ( image/png ), let bytes = req . body . bytes { \n try database . save ( image : bytes ) }", - "title": "Image/PNG" - }, - { - "location": "/http/body/#chunked-case", - "text": "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. 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: return Response ( status : . ok ) { chunker in \n for name in [ joe , pam , cheryl ] { \n sleep ( 1 ) \n try chunker . send ( name ) \n } \n\n try chunker . close () } Make sure to call close() before the chunker leaves scope.", - "title": "Chunked Case" - }, - { - "location": "/http/body/#bodyrepresentable", - "text": "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: 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: public protocol BodyRepresentable { \n func makeBody () - Body }", - "title": "BodyRepresentable" - }, - { - "location": "/http/body/#custom", - "text": "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: extension VPRFile : HTTP . BodyRepresentable { \n func makeBody () - Body { \n // collect bytes \n return . data ( bytes ) \n } } 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 . drop . get ( files , :file-name ) { request in \n let filename = try request . parameters . extract ( file-name ) as String \n let file = VPRFileManager . fetch ( filename ) \n 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 extension VPRFile : HTTP . ResponseRepresentable { \n func makeResponse () - Response { \n return Response ( \n status : . ok , \n headers : [ Content-Type : file/vpr ], \n body : file \n ) \n } } Here's our above example now: drop . get ( files , :file-name ) { request in \n let filename = try request . parameters . extract ( file-name ) as String \n return VPRFileManager . fetch ( filename ) } We could also use type-safe routing to make this even more concise: drop . get ( files , String . self ) { request , filename in \n return VPRFileManager . fetch ( filename ) }", - "title": "Custom" - }, - { - "location": "/http/response-representable/", - "text": "ResponseRepresentable\n\n\nTraditionally HTTP servers take a \nRequest\n and return a \nResponse\n. 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.\n\n\nLet's start with the definition of \nResponseRepresentable\n\n\npublic\n \nprotocol\n \nResponseRepresentable\n \n{\n\n \nfunc\n \nmakeResponse\n()\n \nthrows\n \n-\n \nResponse\n\n\n}\n\n\n\n\n\n\nBy 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):\n\n\nString\n\n\nBecause string conforms to \nResponseRepresentable\n, we can return it directly in a Vapor route handler.\n\n\ndrop\n.\nget\n(\nhello\n)\n \n{\n \nrequest\n \nin\n\n \nreturn\n \nHello, World!\n\n\n}\n\n\n\n\n\n\nJSON\n\n\nJSON\n can be returned directly instead of recreating a response each time.\n\n\ndrop\n.\nget\n(\nhello\n)\n \n{\n \nrequest\n \nin\n\n \nvar\n \njson\n \n=\n \nJSON\n()\n\n \ntry\n \njson\n.\nset\n(\nhello\n,\n \nworld\n)\n\n \ntry\n \njson\n.\nset\n(\nsome-numbers\n,\n \n[\n1\n,\n \n2\n,\n \n3\n])\n\n \nreturn\n \njson\n\n\n}\n\n\n\n\n\n\nResponse\n\n\nOf course, we can also return Responses for anything not covered:\n\n\ndrop\n.\nget\n(\nhello\n)\n \n{\n \nrequest\n \nin\n\n \nreturn\n \nResponse\n(\n\n \nstatus\n:\n \n.\nok\n,\n \n \nheaders\n:\n \n[\nContent-Type\n:\n \ntext/plain\n],\n \n \nbody\n:\n \nHello, World!\n\n \n)\n\n\n}\n\n\n\n\n\n\nConforming\n\n\nAll we need to do to return our own objects is conform them to \nResponseRepresentable\n. Let's look at an example type, a simple blog post model:\n\n\nimport\n \nFoundation\n\n\n\nstruct\n \nBlogPost\n \n{\n\n \nlet\n \nid\n:\n \nString\n\n \nlet\n \ncontent\n:\n \nString\n\n \nlet\n \ncreatedAt\n:\n \nNSDate\n\n\n}\n\n\n\n\n\n\nAnd now, let's conform it to response representable.\n\n\nimport\n \nHTTP\n\n\n\nextension\n \nBlogPost\n:\n \nResponseRepresentable\n \n{\n\n \nfunc\n \nmakeResponse\n()\n \nthrows\n \n-\n \nResponse\n \n{\n\n \nvar\n \njson\n \n=\n \nJSON\n()\n\n \ntry\n \njson\n.\nset\n(\nid\n,\n \nid\n)\n\n \ntry\n \njson\n.\nset\n(\ncontent\n,\n \ncontent\n)\n\n \ntry\n \njson\n.\nset\n(\ncreated-at\n,\n \ncreatedAt\n.\ntimeIntervalSince1970\n)\n\n \nreturn\n \ntry\n \njson\n.\nmakeResponse\n()\n\n \n}\n\n\n}\n\n\n\n\n\n\nNow that we've modeled our BlogPost, we can return it directly in route handlers.\n\n\ndrop\n.\npost\n(\npost\n)\n \n{\n \nreq\n \nin\n\n \nguard\n \nlet\n \ncontent\n \n=\n \nrequest\n.\ndata\n[\ncontent\n]\n \nelse\n \n{\n \n \nthrow\n \nError\n.\nmissingContent\n \n \n}\n\n \nlet\n \npost\n \n=\n \nPost\n(\ncontent\n:\n \ncontent\n)\n\n \ntry\n \npost\n.\nsave\n(\nto\n:\n \ndatabase\n)\n\n \nreturn\n \npost\n\n\n}", - "title": "ResponseRepresentable" - }, - { - "location": "/http/response-representable/#responserepresentable", - "text": "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 public protocol ResponseRepresentable { \n 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):", - "title": "ResponseRepresentable" - }, - { - "location": "/http/response-representable/#string", - "text": "Because string conforms to ResponseRepresentable , we can return it directly in a Vapor route handler. drop . get ( hello ) { request in \n return Hello, World! }", - "title": "String" - }, - { - "location": "/http/response-representable/#json", - "text": "JSON can be returned directly instead of recreating a response each time. drop . get ( hello ) { request in \n var json = JSON () \n try json . set ( hello , world ) \n try json . set ( some-numbers , [ 1 , 2 , 3 ]) \n return json }", - "title": "JSON" - }, - { - "location": "/http/response-representable/#response", - "text": "Of course, we can also return Responses for anything not covered: drop . get ( hello ) { request in \n return Response ( \n status : . ok , \n headers : [ Content-Type : text/plain ], \n body : Hello, World! \n ) }", - "title": "Response" - }, - { - "location": "/http/response-representable/#conforming", - "text": "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: import Foundation struct BlogPost { \n let id : String \n let content : String \n let createdAt : NSDate } And now, let's conform it to response representable. import HTTP extension BlogPost : ResponseRepresentable { \n func makeResponse () throws - Response { \n var json = JSON () \n try json . set ( id , id ) \n try json . set ( content , content ) \n try json . set ( created-at , createdAt . timeIntervalSince1970 ) \n return try json . makeResponse () \n } } Now that we've modeled our BlogPost, we can return it directly in route handlers. drop . post ( post ) { req in \n guard let content = request . data [ content ] else { \n throw Error . missingContent \n } \n let post = Post ( content : content ) \n try post . save ( to : database ) \n return post }", - "title": "Conforming" - }, - { - "location": "/http/responder/", - "text": "Responder\n\n\nThe \nResponder\n is a simple protocol defining the behavior of objects that can accept a \nRequest\n and return a \nResponse\n. Most notably in Vapor, it is the core API endpoint that connects the \nDroplet\n to the \nServer\n. Let's look at the definition:\n\n\npublic\n \nprotocol\n \nResponder\n \n{\n\n \nfunc\n \nrespond\n(\nto\n \nrequest\n:\n \nRequest\n)\n \nthrows\n \n-\n \nResponse\n\n\n}\n\n\n\n\n\n\n\n\nThe responder protocol is most notably related to Droplet and it's relationship with a server. Average users will not likely interact with it much.\n\n\n\n\nSimple\n\n\nOf course, Vapor provides some conveniences for this, and in practice, we will often call:\n\n\ntry\n \ndrop\n.\nrun\n()\n\n\n\n\n\n\nManual\n\n\nAs we just mentioned, the Vapor \nDroplet\n itself conforms to \nResponder\n, connecting it to the \nServer\n. This means if we wanted to serve our droplet manually, we could do:\n\n\nlet\n \nserver\n \n=\n \ntry\n \nServer\nTCPServerStream\n,\n \nParser\nRequest\n,\n \nSerializer\nResponse\n(\nport\n:\n \nport\n)\n\n\ntry\n \nserver\n.\nstart\n(\nresponder\n:\n \ndroplet\n)\n \n{\n \nerror\n \nin\n\n \nprint\n(\nGot error: \n\\(\nerror\n)\n)\n\n\n}\n\n\n\n\n\n\nAdvanced\n\n\nWe can conform our own objects to \nResponder\n and pass them to \nServers\n. Let's look at an example:\n\n\nfinal\n \nclass\n \nResponder\n:\n \nHTTP\n.\nResponder\n \n{\n\n \nfunc\n \nrespond\n(\nto\n \nrequest\n:\n \nRequest\n)\n \nthrows\n \n-\n \nResponse\n \n{\n\n \nlet\n \nbody\n \n=\n \nHello World\n.\nmakeBody\n()\n\n \nreturn\n \nResponse\n(\nbody\n:\n \nbody\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nThis only returns \n\"Hello World\"\n for every request, it's most commonly going to be linked with a router of some type.\n\n\nfinal\n \nclass\n \nResponder\n:\n \nHTTP\n.\nResponder\n \n{\n\n \nlet\n \nrouter\n:\n \nRouter\n \n=\n \n...\n\n\n \nfunc\n \nrespond\n(\nto\n \nrequest\n:\n \nRequest\n)\n \nthrows\n \n-\n \nResponse\n \n{\n\n \nreturn\n \ntry\n \nrouter\n.\nroute\n(\nrequest\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nWe'll then pass this responder to a server and let it go.\n\n\nlet\n \nserver\n \n=\n \ntry\n \nServer\nTCPServerStream\n,\n \nParser\nRequest\n,\n \nSerializer\nResponse\n(\nport\n:\n \nport\n)\n\n\n\nprint\n(\nvisit http://localhost:\n\\(\nport\n)\n/\n)\n\n\ntry\n \nserver\n.\nstart\n(\nresponder\n:\n \nResponder\n())\n \n{\n \nerror\n \nin\n\n \nprint\n(\nGot error: \n\\(\nerror\n)\n)\n\n\n}\n\n\n\n\n\n\nThis can be used as a jumping off point for applications looking to implement features manually.\n\n\nClient\n\n\nThe \nHTTP.Client\n is itself a \nResponder\n although, instead of handling the \nRequest\n itself, it passes it on to the underlying URI.", - "title": "Responder" - }, - { - "location": "/http/responder/#responder", - "text": "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: public protocol Responder { \n 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.", - "title": "Responder" - }, - { - "location": "/http/responder/#simple", - "text": "Of course, Vapor provides some conveniences for this, and in practice, we will often call: try drop . run ()", - "title": "Simple" - }, - { - "location": "/http/responder/#manual", - "text": "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: let server = try Server TCPServerStream , Parser Request , Serializer Response ( port : port ) try server . start ( responder : droplet ) { error in \n print ( Got error: \\( error ) ) }", - "title": "Manual" - }, - { - "location": "/http/responder/#advanced", - "text": "We can conform our own objects to Responder and pass them to Servers . Let's look at an example: final class Responder : HTTP . Responder { \n func respond ( to request : Request ) throws - Response { \n let body = Hello World . makeBody () \n return Response ( body : body ) \n } } This only returns \"Hello World\" for every request, it's most commonly going to be linked with a router of some type. final class Responder : HTTP . Responder { \n let router : Router = ... \n\n func respond ( to request : Request ) throws - Response { \n return try router . route ( request ) \n } } We'll then pass this responder to a server and let it go. let server = try Server TCPServerStream , Parser Request , Serializer Response ( port : port ) print ( visit http://localhost: \\( port ) / ) try server . start ( responder : Responder ()) { error in \n print ( Got error: \\( error ) ) } This can be used as a jumping off point for applications looking to implement features manually.", - "title": "Advanced" - }, - { - "location": "/http/responder/#client", - "text": "The HTTP.Client is itself a Responder although, instead of handling the Request itself, it passes it on to the underlying URI.", - "title": "Client" - }, - { - "location": "/http/client/", - "text": "Client\n\n\nThe client provided by \nHTTP\n is used to make outgoing requests to remote servers. Let's look at a simple outgoing request.\n\n\nQuickStart\n\n\nLet's jump right in to make a simple HTTP Request. Here's a basic \nGET\n request using your Vapor \nDroplet\n.\n\n\nlet\n \nquery\n \n=\n \n...\n\n\nlet\n \nres\n \n=\n \ntry\n \ndrop\n.\nclient\n.\nget\n(\nhttps://api.spotify.com/v1/search?type=artist\nq=\n\\(\nquery\n)\n)\n\n\nprint\n(\nres\n)\n\n\n\n\n\n\nClean Up\n\n\nThe url above can be a little tricky to read, so let's use the query parameter to clean it up a little bit:\n\n\nlet\n \nres\n \n=\n \ntry\n \ndrop\n.\nclient\n.\nget\n(\nhttps://api.spotify.com/v1/search\n,\n \nquery\n:\n \n[\n\n \ntype\n:\n \nartist\n,\n \n \nq\n:\n \nquery\n\n\n])\n\n\n\n\n\n\nContinued\n\n\nIn addition to \nGET\n requests, Vapor's client provides support for most common HTTP functions. \nGET\n, \nPOST\n, \nPUT\n, \nPATCH\n, \nDELETE\n\n\nHeaders\n\n\nYou can also add additional headers to the request.\n\n\ntry\n \ndrop\n.\nclient\n.\nget\n(\nhttp://some-endpoint/json\n,\n \nheaders\n:\n \n[\n\n \nAPI-Key\n:\n \nvapor123\n\n\n])\n\n\n\n\n\n\nCustom Request\n\n\nYou can ask the client to respond to any \nRequest\n that you create. \nThis is useful if you need to add JSON or FormURLEncoded data to the request.\n\n\nlet\n \nreq\n \n=\n \nRequest\n(\nmethod\n:\n \n.\npost\n,\n \nuri\n:\n \nhttp://some-endpoint\n)\n\n\nreq\n.\nformURLEncoded\n \n=\n \nNode\n(\nnode\n:\n \n[\n\n \nemail\n:\n \nmymail@vapor.codes\n\n\n])\n\n\n\ntry\n \ndrop\n.\nclient\n.\nrespond\n(\nto\n:\n \nreq\n)\n\n\n\n\n\n\nRe-usable Connection\n\n\nUp to this point, we've been using \ndrop.client\n which is a \nClientFactory\n. This creates a new client and TCP connection for each request.\n\n\nFor more better performance, you can create an re-use a single client.\n\n\nlet\n \npokemonClient\n \n=\n \ntry\n \ndrop\n.\nclient\n.\nmakeClient\n(\n\n \nscheme\n:\n \nhttp\n,\n \n \nhost\n:\n \npokeapi.co\n,\n\n \nsecurityLayer\n:\n \n.\nnone\n\n\n)\n\n\n\nfor\n \ni\n \nin\n \n0.\n..\n1\n \n{\n\n \nlet\n \nresponse\n \n=\n \ntry\n \npokemonClient\n.\nget\n(\n/api/v2/pokemon/\n,\n \nquery\n:\n \n[\n\n \nlimit\n:\n \n20\n,\n \n \noffset\n:\n \ni\n\n \n])\n\n \nprint\n(\nresponse: \n\\(\nresponse\n)\n)\n\n\n}\n\n\n\n\n\n\n\n\nNote\n\n\nClients created using \n.makeClient\n can not connect to a different server after initialization. (Proxy servers are an exception)\n\n\n\n\nProxy\n\n\nThe \ndrop.client\n can be configured to use a proxy by default.\n\n\nConfig/client.json\n\n\n{\n\n \nproxy\n:\n \n{\n\n \nhostname\n:\n \ngoogle.com\n,\n \n \nport\n:\n \n80\n,\n\n \nsecurityLayer\n:\n \nnone\n\n \n}\n\n\n}\n\n\n\n\n\n\nFor the above example, all requests sent to \ndrop.client.get(...)\n would be proxied through google.com.", - "title": "Client" - }, - { - "location": "/http/client/#client", - "text": "The client provided by HTTP is used to make outgoing requests to remote servers. Let's look at a simple outgoing request.", - "title": "Client" - }, - { - "location": "/http/client/#quickstart", - "text": "Let's jump right in to make a simple HTTP Request. Here's a basic GET request using your Vapor Droplet . let query = ... let res = try drop . client . get ( https://api.spotify.com/v1/search?type=artist q= \\( query ) ) print ( res )", - "title": "QuickStart" - }, - { - "location": "/http/client/#clean-up", - "text": "The url above can be a little tricky to read, so let's use the query parameter to clean it up a little bit: let res = try drop . client . get ( https://api.spotify.com/v1/search , query : [ \n type : artist , \n q : query ])", - "title": "Clean Up" - }, - { - "location": "/http/client/#continued", - "text": "In addition to GET requests, Vapor's client provides support for most common HTTP functions. GET , POST , PUT , PATCH , DELETE", - "title": "Continued" - }, - { - "location": "/http/client/#headers", - "text": "You can also add additional headers to the request. try drop . client . get ( http://some-endpoint/json , headers : [ \n API-Key : vapor123 ])", - "title": "Headers" - }, - { - "location": "/http/client/#custom-request", - "text": "You can ask the client to respond to any Request that you create. \nThis is useful if you need to add JSON or FormURLEncoded data to the request. let req = Request ( method : . post , uri : http://some-endpoint ) req . formURLEncoded = Node ( node : [ \n email : mymail@vapor.codes ]) try drop . client . respond ( to : req )", - "title": "Custom Request" - }, - { - "location": "/http/client/#re-usable-connection", - "text": "Up to this point, we've been using drop.client which is a ClientFactory . This creates a new client and TCP connection for each request. For more better performance, you can create an re-use a single client. let pokemonClient = try drop . client . makeClient ( \n scheme : http , \n host : pokeapi.co , \n securityLayer : . none ) for i in 0. .. 1 { \n let response = try pokemonClient . get ( /api/v2/pokemon/ , query : [ \n limit : 20 , \n offset : i \n ]) \n print ( response: \\( response ) ) } Note Clients created using .makeClient can not connect to a different server after initialization. (Proxy servers are an exception)", - "title": "Re-usable Connection" - }, - { - "location": "/http/client/#proxy", - "text": "The drop.client can be configured to use a proxy by default. Config/client.json { \n proxy : { \n hostname : google.com , \n port : 80 , \n securityLayer : none \n } } For the above example, all requests sent to drop.client.get(...) would be proxied through google.com.", - "title": "Proxy" - }, - { - "location": "/http/server/", - "text": "Server\n\n\nThe server is responsible for accepting connections from clients, parsing their requests, and delivering them a response. \n\n\nDefault\n\n\nStarting your Droplet with a default server is simple.\n\n\nimport\n \nVapor\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n()\n\n\ntry\n \ndrop\n.\nrun\n()\n\n\n\n\n\n\nThe default server will bind to host \n0.0.0.0\n at port \n8080\n.\n\n\nConfig\n\n\nIf you are using a \nConfig/server.json\n file, this is where you can easily change your host and port.\n\n\n{\n\n \nport\n:\n \n$PORT:8080\n,\n\n \nhost\n:\n \n0.0.0.0\n,\n\n \nsecurityLayer\n:\n \nnone\n\n\n}\n\n\n\n\n\n\nThe default \nserver.json\n is above. The port with try to resolve the environment variable \n$PORT\n or fallback to \n8080\n.\n\n\nTLS\n\n\nTLS (formerly SSL) can be configured with a variety of different certificate and signature types.\n\n\nVerify\n\n\nVerificiation of hosts and certificates can be disabled. They are enabled by default.\n\n\n\n\nNote: Be extremely careful when disabling these options.\n\n\n\n\ntls\n:\n \n{\n\n \nverifyHost\n:\n \nfalse\n,\n\n \nverifyCertificates\n:\n \nfalse\n\n\n}\n\n\n\n\n\n\nCertificates\n\n\nNone\n\n\ntls\n:\n \n{\n\n \ncertificates\n:\n \nnone\n\n\n}\n\n\n\n\n\n\nChain\n\n\ntls\n:\n \n{\n\n \ncertificates\n:\n \nchain\n,\n\n \nchainFile\n:\n \n/path/to/chainfile\n\n\n}\n\n\n\n\n\n\nFiles\n\n\ntls\n:\n \n{\n\n \ncertificates\n:\n \nfiles\n,\n\n \ncertificateFile\n:\n \n/path/to/cert.pem\n,\n\n \nprivateKeyFile\n:\n \n/path/to/key.pem\n\n\n}\n\n\n\n\n\n\nCertificate Authority\n\n\ntls\n:\n \n{\n\n \ncertificates\n:\n \nca\n\n\n}\n\n\n\n\n\n\nSignature\n\n\nSelf Signed\n\n\ntls\n:\n \n{\n\n \nsignature\n:\n \nselfSigned\n\n\n}\n\n\n\n\n\n\nSigned File\n\n\ntls\n:\n \n{\n\n \nsignature\n:\n \nsignedFile\n,\n\n \ncaCertificateFile\n:\n \n/path/to/file\n\n\n}\n\n\n\n\n\n\nSigned Directory\n\n\ntls\n:\n \n{\n\n \nsignature\n:\n \nsignedDirectory\n,\n\n \ncaCertificateDirectory\n:\n \n/path/to/dir\n\n\n}\n\n\n\n\n\n\nExample\n\n\nHere is an example \nserver.json\n file using certificate files with a self signed signature and host verification redundantly set to \ntrue\n.\n\n\n{\n\n \nport\n:\n \n8443\n,\n\n \nhost\n:\n \n0.0.0.0\n,\n\n \nsecurityLayer\n:\n \ntls\n,\n\n \ntls\n:\n \n{\n\n \nverifyHost\n:\n \ntrue\n,\n\n \ncertificates\n:\n \nfiles\n,\n\n \ncertificateFile\n:\n \n/vapor/certs/cert.pem\n,\n\n \nprivateKeyFile\n:\n \n/vapor/certs/key.pem\n,\n\n \nsignature\n:\n \nselfSigned\n\n \n}\n\n\n}\n\n\n\n\n\n\nNginx\n\n\nIt is highly recommended that you serve your Vapor project behind Nginx in production. Read more in the \ndeploy Nginx\n section.", - "title": "Server" - }, - { - "location": "/http/server/#server", - "text": "The server is responsible for accepting connections from clients, parsing their requests, and delivering them a response.", - "title": "Server" - }, - { - "location": "/http/server/#default", - "text": "Starting your Droplet with a default server is simple. import Vapor let drop = try Droplet () try drop . run () The default server will bind to host 0.0.0.0 at port 8080 .", - "title": "Default" - }, - { - "location": "/http/server/#config", - "text": "If you are using a Config/server.json file, this is where you can easily change your host and port. { \n port : $PORT:8080 , \n host : 0.0.0.0 , \n securityLayer : none } The default server.json is above. The port with try to resolve the environment variable $PORT or fallback to 8080 .", - "title": "Config" - }, - { - "location": "/http/server/#tls", - "text": "TLS (formerly SSL) can be configured with a variety of different certificate and signature types.", - "title": "TLS" - }, - { - "location": "/http/server/#verify", - "text": "Verificiation of hosts and certificates can be disabled. They are enabled by default. Note: Be extremely careful when disabling these options. tls : { \n verifyHost : false , \n verifyCertificates : false }", - "title": "Verify" - }, - { - "location": "/http/server/#certificates", - "text": "", - "title": "Certificates" - }, - { - "location": "/http/server/#none", - "text": "tls : { \n certificates : none }", - "title": "None" - }, - { - "location": "/http/server/#chain", - "text": "tls : { \n certificates : chain , \n chainFile : /path/to/chainfile }", - "title": "Chain" - }, - { - "location": "/http/server/#files", - "text": "tls : { \n certificates : files , \n certificateFile : /path/to/cert.pem , \n privateKeyFile : /path/to/key.pem }", - "title": "Files" - }, - { - "location": "/http/server/#certificate-authority", - "text": "tls : { \n certificates : ca }", - "title": "Certificate Authority" - }, - { - "location": "/http/server/#signature", - "text": "", - "title": "Signature" - }, - { - "location": "/http/server/#self-signed", - "text": "tls : { \n signature : selfSigned }", - "title": "Self Signed" - }, - { - "location": "/http/server/#signed-file", - "text": "tls : { \n signature : signedFile , \n caCertificateFile : /path/to/file }", - "title": "Signed File" - }, - { - "location": "/http/server/#signed-directory", - "text": "tls : { \n signature : signedDirectory , \n caCertificateDirectory : /path/to/dir }", - "title": "Signed Directory" - }, - { - "location": "/http/server/#example", - "text": "Here is an example server.json file using certificate files with a self signed signature and host verification redundantly set to true . { \n port : 8443 , \n host : 0.0.0.0 , \n securityLayer : tls , \n tls : { \n verifyHost : true , \n certificates : files , \n certificateFile : /vapor/certs/cert.pem , \n privateKeyFile : /vapor/certs/key.pem , \n signature : selfSigned \n } }", - "title": "Example" - }, - { - "location": "/http/server/#nginx", - "text": "It is highly recommended that you serve your Vapor project behind Nginx in production. Read more in the deploy Nginx section.", - "title": "Nginx" - }, - { - "location": "/http/cors/", - "text": "CORS\n\n\nVapor by default provides a middleware for implementing proper support for Cross-Origin Resource Sharing (CORS) named \nCORSMiddleware\n.\n\n\n\"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.\" - \nhttp://enable-cors.org/\n\n\nTo learn more about middlewares, please visit the Middleware section of the documentation \nhere\n.\n\n\n\n\nImage Author: \nWikipedia\n\n\nBasic\n\n\nFirst of all, add the CORS middleware into your droplet middlewares array.\n\n\nConfig/droplet.json\n\n\n{\n\n \n...,\n\n \nmiddleware\n:\n \n[\n\n \n...\n,\n\n \ncors\n,\n\n \n...\n,\n\n \n],\n\n \n...,\n\n\n}\n\n\n\n\n\n\nNext time you boot your application, you will be prompted to add a \nConfig/cors.json\n file.\n\n\nConfig/cors.json\n\n\n{\n\n \nallowedOrigin\n:\n \n*\n,\n\n \nallowedMethods\n:\n \n[\nGET\n,\n \nPOST\n,\n \nPUT\n,\n \nOPTIONS\n,\n \nDELETE\n,\n \nPATCH\n],\n\n \nallowedHeaders\n:\n \n[\n\n \nAccept\n,\n\n \nAuthorization\n,\n\n \nContent-Type\n,\n\n \nOrigin\n,\n\n \nX-Requested-With\n\n \n]\n\n\n}\n\n\n\n\n\n\n\n\nNote: 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.\n\n\n\n\nCORSMiddleware\n has a default configuration which should suit most users, with values as follows:\n\n\n\n\nAllowed Origin\n \n\n\nValue of origin header in the request.\n\n\n\n\n\n\nAllowed Methods\n \n\n\nGET\n, \nPOST\n, \nPUT\n, \nOPTIONS\n, \nDELETE\n, \nPATCH\n\n\n\n\n\n\nAllowed Headers\n\n\nAccept\n, \nAuthorization\n, \nContent-Type\n, \nOrigin\n, \nX-Requested-With\n\n\n\n\n\n\n\n\nAdvanced\n\n\nAll settings and presets can be customized by advanced users. There's two ways of doing this, either you programatically create and configure a \nCORSConfiguration\n object or you can put your configuration into a Vapor's JSON config file.\n\n\nSee below for how to set up both and what are the options.\n\n\nConfiguration\n\n\nThe \nCORSConfiguration\n struct is used to configure the \nCORSMiddleware\n. You can instanitate one like this:\n\n\nlet\n \nconfig\n \n=\n \ntry\n \nConfig\n()\n\n\nconfig\n.\naddConfigurable\n(\nmiddleware\n:\n \n{\n \nconfig\n \nin\n\n \nreturn\n \nCORSConfiguration\n(\n\n \nallowedOrigin\n:\n \n.\ncustom\n(\nhttps://vapor.codes\n),\n\n \nallowedMethods\n:\n \n[.\nget\n,\n \n.\npost\n,\n \n.\noptions\n],\n\n \nallowedHeaders\n:\n \n[\nAccept\n,\n \nAuthorization\n],\n\n \nallowCredentials\n:\n \nfalse\n,\n\n \ncacheExpiration\n:\n \n600\n,\n\n \nexposedHeaders\n:\n \n[\nCache-Control\n,\n \nContent-Language\n]\n\n \n)\n\n\n},\n \nname\n:\n \ncustom-cors\n)\n\n\n\n\n\n\nThen set the \ncustom-cors\n in your Droplet's middleware array.\n\n\nConfig/droplet.json\n\n\n{\n\n \n...,\n\n \nmiddleware\n:\n \n[\n\n \n...\n,\n\n \ncustom-cors\n,\n\n \n...\n,\n\n \n],\n\n \n...,\n\n\n}\n\n\n\n\n\n\n\n\nNote: Please consult the documentation in the source code of the \nCORSConfiguration\n for more information about available values for the settings.", - "title": "CORS" - }, - { - "location": "/http/cors/#cors", - "text": "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/ To learn more about middlewares, please visit the Middleware section of the documentation here . Image Author: Wikipedia", - "title": "CORS" - }, - { - "location": "/http/cors/#basic", - "text": "First of all, add the CORS middleware into your droplet middlewares array. Config/droplet.json { \n ..., \n middleware : [ \n ... , \n cors , \n ... , \n ], \n ..., } Next time you boot your application, you will be prompted to add a Config/cors.json file. Config/cors.json { \n allowedOrigin : * , \n allowedMethods : [ GET , POST , PUT , OPTIONS , DELETE , PATCH ], \n allowedHeaders : [ \n Accept , \n Authorization , \n Content-Type , \n Origin , \n X-Requested-With \n ] } 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", - "title": "Basic" - }, - { - "location": "/http/cors/#advanced", - "text": "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.", - "title": "Advanced" - }, - { - "location": "/http/cors/#configuration", - "text": "The CORSConfiguration struct is used to configure the CORSMiddleware . You can instanitate one like this: let config = try Config () config . addConfigurable ( middleware : { config in \n return CORSConfiguration ( \n allowedOrigin : . custom ( https://vapor.codes ), \n allowedMethods : [. get , . post , . options ], \n allowedHeaders : [ Accept , Authorization ], \n allowCredentials : false , \n cacheExpiration : 600 , \n exposedHeaders : [ Cache-Control , Content-Language ] \n ) }, name : custom-cors ) Then set the custom-cors in your Droplet's middleware array. Config/droplet.json { \n ..., \n middleware : [ \n ... , \n custom-cors , \n ... , \n ], \n ..., } Note: Please consult the documentation in the source code of the CORSConfiguration for more information about available values for the settings.", - "title": "Configuration" - }, - { - "location": "/leaf/package/", - "text": "Using Leaf\n\n\nThis section outlines how to import the Leaf package both with or without a Vapor project.\n\n\nExample Folder Structure\n\n\nHello\n\u251c\u2500\u2500 Config\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 app.json\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 crypto.json\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 droplet.json\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 fluent.json\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 server.json\n\u251c\u2500\u2500 Package.pins\n\u251c\u2500\u2500 Package.swift\n\u251c\u2500\u2500 Public\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 Resources\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 Views\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 hello.leaf\n\u251c\u2500\u2500 Public\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 images (images resources)\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 styles (css resources) \n\u251c\u2500\u2500 Sources\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 App\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 Config+Setup.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 Controllers\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 PostController.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 Droplet+Setup.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 Models\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Post.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Routes.swift\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 Run\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.swift\n\u251c\u2500\u2500 Tests\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 AppTests\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 PostControllerTests.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 RouteTests.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Utilities.swift\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 LinuxMain.swift\n\u251c\u2500\u2500 circle.yml\n\u2514\u2500\u2500 license\n\n\n\n\n\nWith Vapor\n\n\nThe easiest way to use Leaf with Vapor is to include the Leaf provider. \n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/vapor.git\n,\n \nmajorVersion\n:\n \n2\n),\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/leaf-provider.git\n,\n \nmajorVersion\n:\n \n1\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nThe Leaf provider package adds Leaf to your project and adds some additional, Vapor-specific conveniences like \ndrop.stem()\n. \n\n\nUse \nimport LeafProvider\n.\n\n\nJust Leaf\n\n\nAt the core of the Leaf provider is a fast, pure Swift templating engine. You can use it with any of your Swift packages or server-side Swift applications.\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n...\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/leaf.git\n,\n \nmajorVersion\n:\n \n2\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Leaf\n to access the \nLeaf.Stem\n class.", - "title": "Package" - }, - { - "location": "/leaf/package/#using-leaf", - "text": "This section outlines how to import the Leaf package both with or without a Vapor project.", - "title": "Using Leaf" - }, - { - "location": "/leaf/package/#example-folder-structure", - "text": "Hello\n\u251c\u2500\u2500 Config\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 app.json\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 crypto.json\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 droplet.json\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 fluent.json\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 server.json\n\u251c\u2500\u2500 Package.pins\n\u251c\u2500\u2500 Package.swift\n\u251c\u2500\u2500 Public\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 Resources\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 Views\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 hello.leaf\n\u251c\u2500\u2500 Public\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 images (images resources)\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 styles (css resources) \n\u251c\u2500\u2500 Sources\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 App\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 Config+Setup.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 Controllers\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 PostController.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 Droplet+Setup.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 Models\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Post.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Routes.swift\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 Run\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.swift\n\u251c\u2500\u2500 Tests\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 AppTests\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 PostControllerTests.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 RouteTests.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 Utilities.swift\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 LinuxMain.swift\n\u251c\u2500\u2500 circle.yml\n\u2514\u2500\u2500 license", - "title": "Example Folder Structure" - }, - { - "location": "/leaf/package/#with-vapor", - "text": "The easiest way to use Leaf with Vapor is to include the Leaf provider. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n . Package ( url : https://github.com/vapor/vapor.git , majorVersion : 2 ), \n . Package ( url : https://github.com/vapor/leaf-provider.git , majorVersion : 1 ) \n ], \n exclude : [ ... ] ) The Leaf provider package adds Leaf to your project and adds some additional, Vapor-specific conveniences like drop.stem() . Use import LeafProvider .", - "title": "With Vapor" - }, - { - "location": "/leaf/package/#just-leaf", - "text": "At the core of the Leaf provider is a fast, pure Swift templating engine. You can use it with any of your Swift packages or server-side Swift applications. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . Package ( url : https://github.com/vapor/leaf.git , majorVersion : 2 ) \n ], \n exclude : [ ... ] ) Use import Leaf to access the Leaf.Stem class.", - "title": "Just Leaf" - }, - { - "location": "/leaf/provider/", - "text": "Leaf Provider\n\n\nAfter you've \nadded the Leaf Provider package\n to your project, setting the provider up in code is easy.\n\n\nAdd to Droplet\n\n\nFirst, register the \nLeafProvider.Provider\n with your Droplet.\n\n\nimport\n \nVapor\n\n\nimport\n \nLeafProvider\n\n\n\nlet\n \nconfig\n \n=\n \ntry\n \nConfig\n()\n\n\ntry\n \nconfig\n.\naddProvider\n(\nLeafProvider\n.\nProvider\n.\nself\n)\n\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nconfig\n)\n\n\n\n...\n\n\n\n\n\n\nConfigure Droplet\n\n\nOnce the provider is added to your Droplet, you can configure your Droplet to use the Leaf view renderer.\n\n\nConfig/droplet.json\n\n\n{\n\n \n...,\n\n \nview\n:\n \nleaf\n,\n\n \n...\n\n\n}\n\n\n\n\n\n\n\n\nSeealso\n\n\nLearn more about configuration files in the \nSettings guide\n.\n\n\n\n\nManual\n\n\nYou can also set the \ndrop.view\n property manually if you want to hardcode your view renderer.\n\n\nimport\n \nVapor\n\n\nimport\n \nLeafProvider\n\n\n\nlet\n \nview\n \n=\n \nLeafRenderer\n(\nviewsDir\n:\n \ndrop\n.\nviewsDir\n)\n\n\nlet\n \ndrop\n \n=\n \ntry\n \nDroplet\n(\nview\n:\n \nview\n)\n\n\n\n\n\n\nDone\n\n\nNext time you boot your application, your views will be rendered using Leaf.", - "title": "Provider" - }, - { - "location": "/leaf/provider/#leaf-provider", - "text": "After you've added the Leaf Provider package to your project, setting the provider up in code is easy.", - "title": "Leaf Provider" - }, - { - "location": "/leaf/provider/#add-to-droplet", - "text": "First, register the LeafProvider.Provider with your Droplet. import Vapor import LeafProvider let config = try Config () try config . addProvider ( LeafProvider . Provider . self ) let drop = try Droplet ( config ) ...", - "title": "Add to Droplet" - }, - { - "location": "/leaf/provider/#configure-droplet", - "text": "Once the provider is added to your Droplet, you can configure your Droplet to use the Leaf view renderer. Config/droplet.json { \n ..., \n view : leaf , \n ... } Seealso Learn more about configuration files in the Settings guide .", - "title": "Configure Droplet" - }, - { - "location": "/leaf/provider/#manual", - "text": "You can also set the drop.view property manually if you want to hardcode your view renderer. import Vapor import LeafProvider let view = LeafRenderer ( viewsDir : drop . viewsDir ) let drop = try Droplet ( view : view )", - "title": "Manual" - }, - { - "location": "/leaf/provider/#done", - "text": "Next time you boot your application, your views will be rendered using Leaf.", - "title": "Done" - }, - { - "location": "/leaf/leaf/", - "text": "Warning\n\n\nThis section may contain outdated information.\n\n\n\n\nLeaf\n\n\nWelcome to Leaf. Leaf's goal is to be a simple templating language that can make generating views easier. There are plenty of great templating languages, so use what's best for you -- maybe that's Leaf! The goals of Leaf are:\n\n\n\n\nSmall set of strictly enforced rules\n\n\nConsistency\n\n\nParser first mentality\n\n\nExtensibility\n\n\n\n\nSyntax\n\n\nStructure\n\n\nLeaf Tags are made up of 4 Elements:\n - Token: \n#\n is the Token\n - Name: A \nstring\n that identifies the tag\n - Parameter List: \n()\n May accept 0 or more arguments\n - Body (optional): \n{}\n Must be separated from the Parameter List by a space\n\n\nThere 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:\n\n\n\n\n#()\n\n\n#(variable)\n\n\n#import(\"template\")\n\n\n#export(\"link\") { \na href=\"#()\"\n/a\n }\n\n\n#index(friends, \"0\")\n\n\n#loop(friends, \"friend\") { \nli\n#(friend.name)\n/li\n }\n\n\n#raw() { \na href=\"#raw\"\nAnything goes!@#$%^\n*\n/a\n }\n\n\n\n\nUsing the \n#\n token in HTML\n\n\nThe \n#\n token cannot be escaped. Use the \n#()\n or \n#raw() {}\n Tag to output a \n#\n in a Leaf Template. \n#()\n =\n \n#\n\n\nRaw HTML\n\n\nAll Leaf output is escaped by default. Use the \n#raw() {}\n Tag for unescaped output.\n\n#raw() { \na href=\"#link\"\nLink\n/a\n }\n =\n \na href=\"#link\"\nLink\n/a\n\n\n\n\nIMPORTANT! Make sure you are not using the \n#raw() {}\n Tag with user input.\n\n\n\n\nChaining\n\n\nThe double token: \n##\n 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.\n\n\n#if(hasFriends) ##embed(\ngetFriends\n)\n\n\n\n\n\nLeaf's built-in Tags\n\n\nToken: \n#()\n\n\n#() #()hashtags #()FTW =\n # #hashtags #FTW\n\n\n\n\n\nRaw: \n#raw() {}\n\n\n#raw() {\n Do whatever w/ #\ns here, this code won\nt be rendered as leaf document and is not escaped.\n It\ns a great place for things like Javascript or large HTML sections.\n}\n\n\n\n\n\nEqual: \n#equal(lhs, rhs) {}\n\n\n#equal(leaf, leaf) { Leaf == Leaf } =\n Leaf == Leaf\n#equal(leaf, mustache) { Leaf == Mustache } =\n\n\n\n\n\n\nVariable: \n#(variable)\n\n\nHello, #(name)!\n\n\n\n\n\nLoop: \n#loop(array, \"item\") {}\n\n\n#loop(friends, \nfriend\n) {\n #(offset). #(friend.name)\n}\n\n\n\n\n\nThe body of a \n#loop\n can access an \nindex\n variable corresponding to the index of the array. There is also an \noffset\n variable which is the index plus 1.\n\n\nIndex: \n#index(array, _ index: String)\n\n\nHello, #index(friends, \n0\n)!\nHello, #index(friends, \nbest\n)!\n\n\n\n\n\nNote: array indexes are always strings.\n\n\nIf - Else: \n#if(bool) ##else() { this }\n\n\n#if(entering) {\n Hello, there!\n} ##if(leaving) {\n Goodbye!\n} ##else() {\n I\nve been here the whole time.\n}\n\n\n\n\n\nNote that \n#if\n requires a boolean variable. If you need to do a comparison then \n#equal\n is more appropriate. You can chain \n#equal\n in the same way as \n#if\n:\n\n\n#equal(state, \n0\n) {\n \nspan class=\ngreen\nNormal\n/span\n\n} ##equal(state, \n1\n) {\n \nspan class=\norange\nWarning\n/span\n\n} ##else() {\n \nspan class=\nred\nAlert\n/span\n\n}\n\n\n\n\n\nImport: \n#import(\"template\")\n\n\nExport: \n#export(\"template\") { Leaf/HTML }\n\n\nExtend: \n#extend(\"template\")\n\n\nEmbed: \n#embed(\"template\")\n\n\n\n\nWhen using these Layout Tags, omit the template file's .leaf extension.\n\n\n\n\n/// base.leaf\n\n!DOCTYPE html\n\n#import(\nhtml\n)\n\n/// html.leaf\n#extend(\nbase\n)\n\n#export(\nhtml\n) { \nhtml\n#embed(\nbody\n)\n/html\n }\n\n/// body.leaf\n\nbody\n/body\n\n\n\n\n\n\nLeaf renders \nhtml.leaf\n as:\n\n\n!DOCTYPE html\n\n\nhtml\nbody\n/body\n/html\n\n\n\n\n\n\nCustom Tags\n\n\nLook at the existing tags for advanced scenarios, let's look at a basic example by creating \nIndex\n together. This tag will take two arguments, an array, and an index to access.\n\n\nclass\n \nIndex\n:\n \nBasicTag\n \n{\n\n \nlet\n \nname\n \n=\n \nindex\n\n\n \nfunc\n \nrun\n(\narguments\n:\n \n[\nArgument\n])\n \nthrows\n \n-\n \nNode\n?\n \n{\n\n \nguard\n\n \narguments\n.\ncount\n \n==\n \n2\n,\n\n \nlet\n \narray\n \n=\n \narguments\n[\n0\n].\nvalue\n?.\nnodeArray\n,\n\n \nlet\n \nindex\n \n=\n \narguments\n[\n1\n].\nvalue\n?.\nint\n,\n\n \nindex\n \n \narray\n.\ncount\n\n \nelse\n \n{\n \nreturn\n \nnil\n \n}\n\n \nreturn\n \narray\n[\nindex\n]\n\n \n}\n\n\n}\n\n\n\n\n\n\nWe can now register this Tag in our \nmain.swift\n file with:\n\n\nif\n \nlet\n \nleaf\n \n=\n \ndrop\n.\nview\n \nas\n?\n \nLeafRenderer\n \n{\n\n \nleaf\n.\nstem\n.\nregister\n(\nIndex\n())\n\n\n}\n\n\n\n\n\n\nAnd use it just like we did \nabove\n.\n\n\n\n\nNote: Use of non-alphanumeric characters in Tag Names is \nstrongly discouraged\n and may be disallowed in future versions of Leaf.\n\n\n\n\nSyntax Highlighting\n\n\nAtom\n\n\nlanguage-leaf\n by ButkiewiczP\n\n\nXcode\n\n\nIt 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 \n Syntax Coloring \n 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 \nvapor xcode\n is run.\n\n\nThere appears to be a way to \nmake Xcode file associations persist\n but that requires a bit more kung-fu.\n\n\nVS Code\n\n\nhtml-leaf\n by FranciscoAmado\n\n\nCLion \n AppCode\n\n\nSome preliminary work has been done to implement a Leaf Plugin for CLion \n 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 \nVapor Slack", - "title": "Overview" - }, - { - "location": "/leaf/leaf/#leaf", - "text": "Welcome to Leaf. Leaf's goal is to be a simple templating language that can make generating views easier. There are plenty of great templating languages, so use what's best for you -- maybe that's Leaf! The goals of Leaf are: Small set of strictly enforced rules Consistency Parser first mentality Extensibility", - "title": "Leaf" - }, - { - "location": "/leaf/leaf/#syntax", - "text": "", - "title": "Syntax" - }, - { - "location": "/leaf/leaf/#structure", - "text": "Leaf Tags are made up of 4 Elements:\n - Token: # is the Token\n - Name: A string that identifies the tag\n - Parameter List: () May accept 0 or more arguments\n - 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 }", - "title": "Structure" - }, - { - "location": "/leaf/leaf/#using-the-token-in-html", - "text": "The # token cannot be escaped. Use the #() or #raw() {} Tag to output a # in a Leaf Template. #() = #", - "title": "Using the # token in HTML" - }, - { - "location": "/leaf/leaf/#raw-html", - "text": "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.", - "title": "Raw HTML" - }, - { - "location": "/leaf/leaf/#chaining", - "text": "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 )", - "title": "Chaining" - }, - { - "location": "/leaf/leaf/#leafs-built-in-tags", - "text": "", - "title": "Leaf's built-in Tags" - }, - { - "location": "/leaf/leaf/#token", - "text": "#() #()hashtags #()FTW = # #hashtags #FTW", - "title": "Token: #()" - }, - { - "location": "/leaf/leaf/#raw-raw", - "text": "#raw() {\n Do whatever w/ # s here, this code won t be rendered as leaf document and is not escaped.\n It s a great place for things like Javascript or large HTML sections.\n}", - "title": "Raw: #raw() {}" - }, - { - "location": "/leaf/leaf/#equal-equallhs-rhs", - "text": "#equal(leaf, leaf) { Leaf == Leaf } = Leaf == Leaf\n#equal(leaf, mustache) { Leaf == Mustache } =", - "title": "Equal: #equal(lhs, rhs) {}" - }, - { - "location": "/leaf/leaf/#variable-variable", - "text": "Hello, #(name)!", - "title": "Variable: #(variable)" - }, - { - "location": "/leaf/leaf/#loop-looparray-item", - "text": "#loop(friends, friend ) {\n #(offset). #(friend.name)\n} The body of a #loop can access an index variable corresponding to the index of the array. There is also an offset variable which is the index plus 1.", - "title": "Loop: #loop(array, \"item\") {}" - }, - { - "location": "/leaf/leaf/#index-indexarray-_-index-string", - "text": "Hello, #index(friends, 0 )!\nHello, #index(friends, best )! Note: array indexes are always strings.", - "title": "Index: #index(array, _ index: String)" - }, - { - "location": "/leaf/leaf/#if-else-ifbool-else-this", - "text": "#if(entering) {\n Hello, there!\n} ##if(leaving) {\n Goodbye!\n} ##else() {\n I ve been here the whole time.\n} Note that #if requires a boolean variable. If you need to do a comparison then #equal is more appropriate. You can chain #equal in the same way as #if : #equal(state, 0 ) {\n span class= green Normal /span \n} ##equal(state, 1 ) {\n span class= orange Warning /span \n} ##else() {\n span class= red Alert /span \n}", - "title": "If - Else: #if(bool) ##else() { this }" - }, - { - "location": "/leaf/leaf/#import-importtemplate", - "text": "", - "title": "Import: #import(\"template\")" - }, - { - "location": "/leaf/leaf/#export-exporttemplate-leafhtml", - "text": "", - "title": "Export: #export(\"template\") { Leaf/HTML }" - }, - { - "location": "/leaf/leaf/#extend-extendtemplate", - "text": "", - "title": "Extend: #extend(\"template\")" - }, - { - "location": "/leaf/leaf/#embed-embedtemplate", - "text": "When using these Layout Tags, omit the template file's .leaf extension. /// base.leaf !DOCTYPE html \n#import( html )\n\n/// html.leaf\n#extend( base )\n\n#export( html ) { html #embed( body ) /html }\n\n/// body.leaf body /body Leaf renders html.leaf as: !DOCTYPE html html body /body /html", - "title": "Embed: #embed(\"template\")" - }, - { - "location": "/leaf/leaf/#custom-tags", - "text": "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. class Index : BasicTag { \n let name = index \n\n func run ( arguments : [ Argument ]) throws - Node ? { \n guard \n arguments . count == 2 , \n let array = arguments [ 0 ]. value ?. nodeArray , \n let index = arguments [ 1 ]. value ?. int , \n index array . count \n else { return nil } \n return array [ index ] \n } } We can now register this Tag in our main.swift file with: if let leaf = drop . view as ? LeafRenderer { \n leaf . stem . register ( Index ()) } And use it just like we did above . Note: Use of non-alphanumeric characters in Tag Names is strongly discouraged and may be disallowed in future versions of Leaf.", - "title": "Custom Tags" - }, - { - "location": "/leaf/leaf/#syntax-highlighting", - "text": "", - "title": "Syntax Highlighting" - }, - { - "location": "/leaf/leaf/#atom", - "text": "language-leaf by ButkiewiczP", - "title": "Atom" - }, - { - "location": "/leaf/leaf/#xcode", - "text": "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 but that requires a bit more kung-fu.", - "title": "Xcode" - }, - { - "location": "/leaf/leaf/#vs-code", - "text": "html-leaf by FranciscoAmado", - "title": "VS Code" - }, - { - "location": "/leaf/leaf/#clion-appcode", - "text": "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", - "title": "CLion & AppCode" - }, - { - "location": "/validation/package/", - "text": "Work in Progress\n\n\nThe subject of this page is Work in Progress and is not recommended for Production use.\n\n\n\n\n\n\nOutdated\n\n\nThis page contains outdated information.\n\n\n\n\nUsing Validation\n\n\nThis section outlines how to import the Validation package both with or without a Vapor project.\n\n\nWith Vapor\n\n\nThe easiest way to use Validation with Vapor is to include the Validation provider. \n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/vapor.git\n,\n \nmajorVersion\n:\n \n2\n),\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/validation-provider.git\n,\n \nmajorVersion\n:\n \n1\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nThe Validation provider package adds Validation to your project and adds some additional, vapor-specific conveniences like validation middleware. \n\n\nUsing \nimport ValidationProvider\n will import the Validation middleware and the Validation module. \n\n\nJust Validation\n\n\nAt the core of the Validation provider is a Validation module.\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n...\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/validation.git\n,\n \nmajorVersion\n:\n \n1\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Validation\n to access the core validation class.", - "title": "Package" - }, - { - "location": "/validation/package/#using-validation", - "text": "This section outlines how to import the Validation package both with or without a Vapor project.", - "title": "Using Validation" - }, - { - "location": "/validation/package/#with-vapor", - "text": "The easiest way to use Validation with Vapor is to include the Validation provider. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n . Package ( url : https://github.com/vapor/vapor.git , majorVersion : 2 ), \n . Package ( url : https://github.com/vapor/validation-provider.git , majorVersion : 1 ) \n ], \n exclude : [ ... ] ) The Validation provider package adds Validation to your project and adds some additional, vapor-specific conveniences like validation middleware. Using import ValidationProvider will import the Validation middleware and the Validation module.", - "title": "With Vapor" - }, - { - "location": "/validation/package/#just-validation", - "text": "At the core of the Validation provider is a Validation module. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . Package ( url : https://github.com/vapor/validation.git , majorVersion : 1 ) \n ], \n exclude : [ ... ] ) Use import Validation to access the core validation class.", - "title": "Just Validation" - }, - { - "location": "/validation/overview/", - "text": "Work in Progress\n\n\nThe subject of this page is Work in Progress and is not recommended for Production use.\n\n\n\n\n\n\nOutdated\n\n\nThis page contains outdated information.\n\n\n\n\nValidation\n\n\nVapor provides a few different ways to validate data coming into your application. Let's start by looking at the most common.\n\n\nCommon Usage\n\n\nSeveral 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.\n\n\nLet's look at the most common way to validate data.\n\n\nclass\n \nEmployee\n \n{\n\n \nvar\n \nemail\n:\n \nValid\nEmail\n\n \nvar\n \nname\n:\n \nValid\nName\n\n\n \ninit\n(\nrequest\n:\n \nRequest\n)\n \nthrows\n \n{\n\n \nname\n \n=\n \ntry\n \nrequest\n.\ndata\n[\nname\n].\nvalidated\n()\n\n \nemail\n \n=\n \ntry\n \nrequest\n.\ndata\n[\nemail\n].\nvalidated\n()\n\n \n}\n\n\n}\n\n\n\n\n\n\nHere we have a typical Employee model with an \nemail\n and \nname\n property. By declaring both of these properties as \nValid\n, 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.\n\n\nTo store something in a \nValid\n property, you must use the \n.validated()\n method. This is available for any data returned by \nrequest.data\n.\n\n\nEmail\n is a real \nvalidator\n included with Vapor, but \nName\n is not. Let's take a look at how you can create a Validator.\n\n\nValid\nOnlyAlphanumeric\n\n\nValid\nEmail\n\n\nValid\nUnique\nT\n\n\nValid\nMatches\nT\n\n\nValid\nIn\nT\n\n\nValid\nContains\nT\n\n\nValid\nCount\nT\n\n\n\n\n\n\nValidators vs. ValidationSuites\n\n\nValidators, like \nCount\n or \nContains\n can have multiple configurations. For example:\n\n\nlet\n \nname\n:\n \nValid\nCount\nString\n \n=\n \ntry\n \nVapor\n.\nvalidated\n(\nby\n:\n \nCount\n.\nmax\n(\n5\n))\n\n\n\n\n\n\nHere we are validating that the \nString\n is at most 5 characters long. The type of \nValid\nCount\n 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.\n\n\nBecause of this, \nValidators\n themselves are not as type safe as some applications might desire. \nValidationSuites\n fix this. They combine multiple \nValidators\n and/or \nValidationSuites\n together to represent exactly what type of data should be considered valid.\n\n\nCustom Validator\n\n\nHere is how to create a custom \nValidationSuite\n.\n\n\nclass\n \nName\n:\n \nValidationSuite\n \n{\n\n \nstatic\n \nfunc\n \nvalidate\n(\ninput\n \nvalue\n:\n \nString\n)\n \nthrows\n \n{\n\n \nlet\n \nevaluation\n \n=\n \nOnlyAlphanumeric\n.\nself\n\n \n \nCount\n.\nmin\n(\n5\n)\n\n \n \nCount\n.\nmax\n(\n20\n)\n\n\n \ntry\n \nevaluation\n.\nvalidate\n(\ninput\n:\n \nvalue\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nYou 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 \nName\n as only accepting alphanumeric Strings that are between 5 and 20 characters.\n\n\nNow we can be sure that anything of type \nValid\nName\n follows these rules.\n\n\nCombining Validators\n\n\nIn the \nName\n validator, you can see that \n is being used to combine validators. You can use \n as well as \n||\n to combine any validator as you would boolean values with an \nif\n statement.\n\n\nYou can also use \n!\n to invert the validator.\n\n\nlet\n \nsymbols\n \n=\n \ninput\n.\nvalidated\n(\nby\n:\n \n!\nOnlyAlphanumeric\n.\nself\n)\n\n\n\n\n\n\nTesting Validity\n\n\nWhile \nvalidated() throw\n is the most common method for validating, there are two others.\n\n\nlet\n \npassed\n \n=\n \ninput\n.\npasses\n(\nCount\n.\nmin\n(\n5\n))\n\n\nlet\n \nvalid\n \n=\n \ntry\n \ninput\n.\ntested\n(\nCount\n.\nmin\n(\n5\n))\n\n\n\n\n\n\npasses()\n returns a boolean indicating whether or not the test passed. \ntested()\n will throw if the validation does not pass. But unlike \nvalidated()\n which returns a \nValid\n type, \ntested()\n returns the original type of the item it was called on.\n\n\nValidation Failures\n\n\nVapor will automatically catch validation failures in the \nValidationMiddleware\n. But you can catch them on your own, or customize responses for certain types of failures.\n\n\ndo\n \n{\n\n \n//validation here\n\n\n}\n \ncatch\n \nlet\n \nerror\n \nas\n \nValidationErrorProtocol\n \n{\n\n \nprint\n(\nerror\n.\nmessage\n)\n\n\n}", - "title": "Overview" - }, - { - "location": "/validation/overview/#validation", - "text": "Vapor provides a few different ways to validate data coming into your application. Let's start by looking at the most common.", - "title": "Validation" - }, - { - "location": "/validation/overview/#common-usage", - "text": "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. class Employee { \n var email : Valid Email \n var name : Valid Name \n\n init ( request : Request ) throws { \n name = try request . data [ name ]. validated () \n email = try request . data [ email ]. validated () \n } } 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. Valid OnlyAlphanumeric Valid Email Valid Unique T Valid Matches T Valid In T Valid Contains T Valid Count T", - "title": "Common Usage" - }, - { - "location": "/validation/overview/#validators-vs-validationsuites", - "text": "Validators, like Count or Contains can have multiple configurations. For example: 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.", - "title": "Validators vs. ValidationSuites" - }, - { - "location": "/validation/overview/#custom-validator", - "text": "Here is how to create a custom ValidationSuite . class Name : ValidationSuite { \n static func validate ( input value : String ) throws { \n let evaluation = OnlyAlphanumeric . self \n Count . min ( 5 ) \n Count . max ( 20 ) \n\n try evaluation . validate ( input : value ) \n } } 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.", - "title": "Custom Validator" - }, - { - "location": "/validation/overview/#combining-validators", - "text": "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. let symbols = input . validated ( by : ! OnlyAlphanumeric . self )", - "title": "Combining Validators" - }, - { - "location": "/validation/overview/#testing-validity", - "text": "While validated() throw is the most common method for validating, there are two others. 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.", - "title": "Testing Validity" - }, - { - "location": "/validation/overview/#validation-failures", - "text": "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. do { \n //validation here } catch let error as ValidationErrorProtocol { \n print ( error . message ) }", - "title": "Validation Failures" - }, - { - "location": "/node/package/", - "text": "Using Node\n\n\nWith Vapor\n\n\nThis package is included with Vapor by default, just add:\n\n\nimport\n \nNode\n\n\n\n\n\n\nWithout Vapor\n\n\nNode provides a lot of conveniences for any server-side, or client side Swift project. To include it in your package, add the following to your \nPackage.swift\n file.\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n...\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/node.git\n,\n \nmajorVersion\n:\n \n2\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Node\n to access Node's APIs", - "title": "Package" - }, - { - "location": "/node/package/#using-node", - "text": "", - "title": "Using Node" - }, - { - "location": "/node/package/#with-vapor", - "text": "This package is included with Vapor by default, just add: import Node", - "title": "With Vapor" - }, - { - "location": "/node/package/#without-vapor", - "text": "Node provides a lot of conveniences for any server-side, or client side Swift project. To include it in your package, add the following to your Package.swift file. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . Package ( url : https://github.com/vapor/node.git , majorVersion : 2 ) \n ], \n exclude : [ ... ] ) Use import Node to access Node's APIs", - "title": "Without Vapor" - }, - { - "location": "/node/getting-started/", - "text": "Getting Started\n\n\nWhy do we have Node?\n\n\nThe web is very stringy, Swift is very type-safe, this is a major problem when doing web development in Swift. Node is our attempt at providing a solution to this problem.\n\n\nWhat is Node?\n\n\nNode is a data abstraction with an emphasis on being an intermediary between distinct types. For example, json from a client might use node to convert between the JSON and itself.\n\n\nHow do I use it?\n\n\nNode can be a little different to work with at first if you're familiar with less type-safe languages, let's look at a couple of examples and how we might start using Node in our projects. Most often, we'll be working with Node conversions.\n\n\nNodeInitializable\n\n\nNodeInitializable can be read and understood as \nAn object that can be initialized with a Node\n. Let's look at a simple implementation.\n\n\nstruct\n \nPerson\n:\n \nNodeInitializable\n \n{\n\n \nlet\n \nname\n:\n \nString\n\n \nlet\n \nage\n:\n \nInt\n\n\n \ninit\n(\nnode\n:\n \nNode\n)\n \nthrows\n \n{\n\n \nname\n \n=\n \ntry\n \nnode\n.\nget\n(\nname\n)\n\n \nage\n \n=\n \ntry\n \nnode\n.\nget\n(\nage\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nNow that we have this, we can easily convert abstract data to a Person. Here's how that might look:\n\n\nlet\n \nperson\n \n=\n \ntry\n \nPerson\n(\nnode\n:\n \njson\n)\n\n\n\n\n\n\n\n\nNote: There are some more advanced functionality options for JSON and database Row types in particular, we'll cover that later.\n\n\n\n\nBy conforming our \nPerson\n object to \nNodeInitializable\n, we can also use more advanced cases such as arrays:\n\n\nlet\n \npeople\n \n=\n \ntry\n \n[\nPerson\n](\nnode\n:\n \njsonArray\n)\n\n\n\n\n\n\nNodeRepresentable\n\n\nNodeRepresentable can be read and understood as \nAn object that can be represented as a Node\n. Let's take a look at a simple implementation. We'll stick with the \nPerson\n example above\n\n\nextension\n \nPerson\n:\n \nNodeRepresentable\n \n{\n\n \nfunc\n \nmakeNode\n(\nin\n \ncontext\n:\n \nContext\n)\n \nthrows\n \n-\n \nNode\n \n{\n\n \nvar\n \nnode\n \n=\n \nNode\n(\ncontext\n)\n\n \ntry\n \nnode\n.\nset\n(\nname\n,\n \nname\n)\n\n \ntry\n \nnode\n.\nset\n(\nage\n,\n \nage\n)\n\n \nreturn\n \nnode\n\n \n}\n\n\n}\n\n\n\n\n\n\nNow that we've done this, we can easily convert our \nperson\n or a collection of \nPerson\n objects into a \nNode\n.\n\n\nlet\n \nnode\n \n=\n \ntry\n \nperson\n.\nmakeNode\n(\nin\n:\n \nnil\n)\n\n\n\n\n\n\nAnd also for collections, like arrays\n\n\nlet\n \nnode\n \n=\n \ntry\n \n[\nkim\n,\n \njoe\n,\n \njan\n].\nmakeNode\n(\nin\n:\n \nnil\n)\n\n\n\n\n\n\nContext\n\n\nUp to this point, we've seen \nContext\n a lot, but what's it for. When we're serializing or mapping an object, we might have a lot of different situations we're mapping differently for. Maybe one is for the database, one is for the view, one is for JSON, etc.\n\n\nIf you're using Vapor, we provide a lot of contexts and more native integration options, but here's how one might define their own.\n\n\nimport\n \nNode\n\n\n\nfinal\n \nclass\n \nMyContext\n:\n \nContext\n \n{\n\n\n}\n\n\n\nlet\n \nmyContext\n \n=\n \nMyContext\n()\n\n\n\nextension\n \nContext\n \n{\n\n \nvar\n \nisMyContext\n:\n \nBool\n \n{\n\n \nreturn\n \nself\n \nis\n \nMyContext\n\n \n}\n\n\n}\n\n\n\n\n\n\nNow inside our object, we could add special behavior.\n\n\nextension\n \nPerson\n:\n \nNodeRepresentable\n \n{\n\n \nfunc\n \nmakeNode\n(\nin\n \ncontext\n:\n \nContext\n)\n \nthrows\n \n-\n \nNode\n \n{\n\n \nvar\n \nnode\n \n=\n \nNode\n(\ncontext\n)\n\n \ntry\n \nnode\n.\nset\n(\nname\n,\n \nname\n)\n\n \ntry\n \nnode\n.\nset\n(\nage\n,\n \nage\n)\n\n \nif\n \ncontext\n.\nisMyContext\n \n{\n\n \ntry\n \nnode\n.\nset\n(\nspecial-attribute\n,\n \nspecial\n)\n\n \n}\n\n \nreturn\n \nnode\n\n \n}\n\n\n}\n\n\n\n\n\n\nWe might call it like this:\n\n\nlet\n \nspecialNode\n \n=\n \nperson\n.\nmakeNode\n(\nin\n:\n \nmyContext\n)\n\n\n\n\n\n\nThis is a common usage, but can be adapted for any scenario where we require special metadata to help us properly serialize or map our object.\n\n\nNodeConvertible\n\n\nNodeConvertible is simply the combination of Representable and Initializable. These objects can be converted easily to and from node. Taking our Person object from earlier, we should be able to do this:\n\n\n// ..\n\n\nlet\n \nnode\n \n=\n \nperson\n.\nmakeNode\n(\nin\n:\n \nmyContext\n)\n\n\nlet\n \nback\n \n=\n \ntry\n \nPerson\n(\nnode\n:\n \nnode\n)\n\n\nprint\n(\n\\(\nperson\n)\n went to node and back to \n\\(\nback\n)\n)", - "title": "Getting Started" - }, - { - "location": "/node/getting-started/#getting-started", - "text": "", - "title": "Getting Started" - }, - { - "location": "/node/getting-started/#why-do-we-have-node", - "text": "The web is very stringy, Swift is very type-safe, this is a major problem when doing web development in Swift. Node is our attempt at providing a solution to this problem.", - "title": "Why do we have Node?" - }, - { - "location": "/node/getting-started/#what-is-node", - "text": "Node is a data abstraction with an emphasis on being an intermediary between distinct types. For example, json from a client might use node to convert between the JSON and itself.", - "title": "What is Node?" - }, - { - "location": "/node/getting-started/#how-do-i-use-it", - "text": "Node can be a little different to work with at first if you're familiar with less type-safe languages, let's look at a couple of examples and how we might start using Node in our projects. Most often, we'll be working with Node conversions.", - "title": "How do I use it?" - }, - { - "location": "/node/getting-started/#nodeinitializable", - "text": "NodeInitializable can be read and understood as An object that can be initialized with a Node . Let's look at a simple implementation. struct Person : NodeInitializable { \n let name : String \n let age : Int \n\n init ( node : Node ) throws { \n name = try node . get ( name ) \n age = try node . get ( age ) \n } } Now that we have this, we can easily convert abstract data to a Person. Here's how that might look: let person = try Person ( node : json ) Note: There are some more advanced functionality options for JSON and database Row types in particular, we'll cover that later. By conforming our Person object to NodeInitializable , we can also use more advanced cases such as arrays: let people = try [ Person ]( node : jsonArray )", - "title": "NodeInitializable" - }, - { - "location": "/node/getting-started/#noderepresentable", - "text": "NodeRepresentable can be read and understood as An object that can be represented as a Node . Let's take a look at a simple implementation. We'll stick with the Person example above extension Person : NodeRepresentable { \n func makeNode ( in context : Context ) throws - Node { \n var node = Node ( context ) \n try node . set ( name , name ) \n try node . set ( age , age ) \n return node \n } } Now that we've done this, we can easily convert our person or a collection of Person objects into a Node . let node = try person . makeNode ( in : nil ) And also for collections, like arrays let node = try [ kim , joe , jan ]. makeNode ( in : nil )", - "title": "NodeRepresentable" - }, - { - "location": "/node/getting-started/#context", - "text": "Up to this point, we've seen Context a lot, but what's it for. When we're serializing or mapping an object, we might have a lot of different situations we're mapping differently for. Maybe one is for the database, one is for the view, one is for JSON, etc. If you're using Vapor, we provide a lot of contexts and more native integration options, but here's how one might define their own. import Node final class MyContext : Context { } let myContext = MyContext () extension Context { \n var isMyContext : Bool { \n return self is MyContext \n } } Now inside our object, we could add special behavior. extension Person : NodeRepresentable { \n func makeNode ( in context : Context ) throws - Node { \n var node = Node ( context ) \n try node . set ( name , name ) \n try node . set ( age , age ) \n if context . isMyContext { \n try node . set ( special-attribute , special ) \n } \n return node \n } } We might call it like this: let specialNode = person . makeNode ( in : myContext ) This is a common usage, but can be adapted for any scenario where we require special metadata to help us properly serialize or map our object.", - "title": "Context" - }, - { - "location": "/node/getting-started/#nodeconvertible", - "text": "NodeConvertible is simply the combination of Representable and Initializable. These objects can be converted easily to and from node. Taking our Person object from earlier, we should be able to do this: // .. let node = person . makeNode ( in : myContext ) let back = try Person ( node : node ) print ( \\( person ) went to node and back to \\( back ) )", - "title": "NodeConvertible" - }, - { - "location": "/core/package/", - "text": "Using Core\n\n\nWith Vapor\n\n\nThis package is included with Vapor by default, just add:\n\n\nimport\n \nCore\n\n\n\n\n\n\nWithout Vapor\n\n\nCore provides a lot of conveniences for any server-side Swift project. To include it in your package, add the following to your \nPackage.swift\n file.\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n...\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/core.git\n,\n \nmajorVersion\n:\n \n2\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Core\n to access Core's APIs.", - "title": "Package" - }, - { - "location": "/core/package/#using-core", - "text": "", - "title": "Using Core" - }, - { - "location": "/core/package/#with-vapor", - "text": "This package is included with Vapor by default, just add: import Core", - "title": "With Vapor" - }, - { - "location": "/core/package/#without-vapor", - "text": "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. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . Package ( url : https://github.com/vapor/core.git , majorVersion : 2 ) \n ], \n exclude : [ ... ] ) Use import Core to access Core's APIs.", - "title": "Without Vapor" - }, - { - "location": "/core/overview/", - "text": "Core\n\n\nCore provides some conveniences for common tasks.\n\n\nBackground\n\n\nEasily create a background thread using \nbackground()\n\n\nprint\n(\nhello\n)\n\n\n\ntry\n \nbackground\n \n{\n\n \nprint\n(\nworld\n)\n \n\n}\n\n\n\n\n\n\nPortal\n\n\nPortals allow you to make async tasks blocking.\n\n\nlet\n \nresult\n \n=\n \ntry\n \nPortal\n.\nopen\n \n{\n \nportal\n \nin\n\n \nsomeAsyncTask\n \n{\n \nresult\n \nin\n\n \nportal\n.\nclose\n(\nwith\n:\n \nresult\n)\n\n \n}\n\n\n}\n\n\n\nprint\n(\nresult\n)\n \n// the result from the async task\n\n\n\n\n\n\nRFC1123\n\n\nCreate RFC1123 type dates.\n\n\nlet\n \nnow\n \n=\n \nDate\n().\nrfc1123\n \n// string \n\n\n\n\n\n\nYou can also parse RFC1123 strings.\n\n\nlet parsed = Date(rfc1123: \nMon, 10 Apr 2017 11:26:13 GMT\n)", - "title": "Overview" - }, - { - "location": "/core/overview/#core", - "text": "Core provides some conveniences for common tasks.", - "title": "Core" - }, - { - "location": "/core/overview/#background", - "text": "Easily create a background thread using background() print ( hello ) try background { \n print ( world ) }", - "title": "Background" - }, - { - "location": "/core/overview/#portal", - "text": "Portals allow you to make async tasks blocking. let result = try Portal . open { portal in \n someAsyncTask { result in \n portal . close ( with : result ) \n } } print ( result ) // the result from the async task", - "title": "Portal" - }, - { - "location": "/core/overview/#rfc1123", - "text": "Create RFC1123 type dates. let now = Date (). rfc1123 // string You can also parse RFC1123 strings. let parsed = Date(rfc1123: Mon, 10 Apr 2017 11:26:13 GMT )", - "title": "RFC1123" - }, - { - "location": "/bits/package/", - "text": "Using Bits\n\n\nWith Vapor\n\n\nThis package is included with Vapor by default, just add:\n\n\nimport\n \nBits\n\n\n\n\n\n\nWithout Vapor\n\n\nBits provides a lot of byte-manipulation conveniences for any server-side Swift project. To include it in your package, add the following to your \nPackage.swift\n file.\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n...\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/bits.git\n,\n \nmajorVersion\n:\n \n1\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Bits\n to access Bits' APIs.", - "title": "Package" - }, - { - "location": "/bits/package/#using-bits", - "text": "", - "title": "Using Bits" - }, - { - "location": "/bits/package/#with-vapor", - "text": "This package is included with Vapor by default, just add: import Bits", - "title": "With Vapor" - }, - { - "location": "/bits/package/#without-vapor", - "text": "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. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . Package ( url : https://github.com/vapor/bits.git , majorVersion : 1 ) \n ], \n exclude : [ ... ] ) Use import Bits to access Bits' APIs.", - "title": "Without Vapor" - }, - { - "location": "/bits/overview/", - "text": "Bits\n\n\nThe bits package is included in Vapor by default and provides a convenient API for working with bytes.\n\n\nTypealias\n\n\nThe bits package provides two type-aliases for bytes.\n\n\ntypealias\n \nByte\n \n=\n \nUInt8\n\n\ntypealias\n \nBytes\n \n=\n \n[\nByte\n]\n\n\n\n\n\n\nBytesConvertible\n\n\nIt'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.\n\n\nlet\n \nhello\n \n=\n \nString\n(\nbytes\n:\n \n[\n72\n,\n \n101\n,\n \n108\n,\n \n108\n,\n \n111\n])\n\n\nlet\n \nbytes\n \n=\n \nhello\n.\nmakeBytes\n()\n \n\n\n\n\n\nString\n\n\nConverting from bytes to string using the UTF-8 encoding is easy.\n\n\nlet\n \nbytes\n \n=\n \nhello\n.\nmakeBytes\n()\n\n\nlet\n \nstring\n \n=\n \nbytes\n.\nmakeString\n()\n\n\nprint\n(\nstring\n)\n \n// \nhello\n\n\n\n\n\n\nByte\n\n\nThe upper and lowercase latin alphabet and some additional control characters are statically typed on the \nByte\n.\n\n\nlet\n \nbytes\n:\n \nBytes\n \n=\n \n[.\nh\n,\n \n.\ne\n,\n \n.\nl\n,\n \n.\nl\n,\n \n.\no\n]\n\n\nprint\n(\nbytes\n.\nmakeString\n())\n \n// \nhello\n\n\n\n\n\n\nThis makes byte manipulation and comparison easy and is useful for building things like parsers and serializers.\n\n\nlet\n \nbyte\n:\n \nByte\n \n=\n \n65\n\n\nif\n \nbyte\n \n==\n \n.\nA\n \n{\n\n \nprint\n(\nfound A!\n)\n\n\n}", - "title": "Overview" - }, - { - "location": "/bits/overview/#bits", - "text": "The bits package is included in Vapor by default and provides a convenient API for working with bytes.", - "title": "Bits" - }, - { - "location": "/bits/overview/#typealias", - "text": "The bits package provides two type-aliases for bytes. typealias Byte = UInt8 typealias Bytes = [ Byte ]", - "title": "Typealias" - }, - { - "location": "/bits/overview/#bytesconvertible", - "text": "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. let hello = String ( bytes : [ 72 , 101 , 108 , 108 , 111 ]) let bytes = hello . makeBytes ()", - "title": "BytesConvertible" - }, - { - "location": "/bits/overview/#string", - "text": "Converting from bytes to string using the UTF-8 encoding is easy. let bytes = hello . makeBytes () let string = bytes . makeString () print ( string ) // hello", - "title": "String" - }, - { - "location": "/bits/overview/#byte", - "text": "The upper and lowercase latin alphabet and some additional control characters are statically typed on the Byte . 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. let byte : Byte = 65 if byte == . A { \n print ( found A! ) }", - "title": "Byte" - }, - { - "location": "/debugging/package/", - "text": "Using Debugging\n\n\nWith Vapor\n\n\nThis package is included with Vapor by default, just add:\n\n\nimport\n \nDebugging\n\n\n\n\n\n\nWithout Vapor\n\n\nDebugging is a convenient protocol for providing more information about error messages. You can use it in any of your Swift projects.\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nProject\n,\n\n \ndependencies\n:\n \n[\n\n \n...\n\n \n.\nPackage\n(\nurl\n:\n \nhttps://github.com/vapor/debugging.git\n,\n \nmajorVersion\n:\n \n1\n)\n\n \n],\n\n \nexclude\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Debugging\n to access Debugging' APIs.", - "title": "Package" - }, - { - "location": "/debugging/package/#using-debugging", - "text": "", - "title": "Using Debugging" - }, - { - "location": "/debugging/package/#with-vapor", - "text": "This package is included with Vapor by default, just add: import Debugging", - "title": "With Vapor" - }, - { - "location": "/debugging/package/#without-vapor", - "text": "Debugging is a convenient protocol for providing more information about error messages. You can use it in any of your Swift projects. import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . Package ( url : https://github.com/vapor/debugging.git , majorVersion : 1 ) \n ], \n exclude : [ ... ] ) Use import Debugging to access Debugging' APIs.", - "title": "Without Vapor" - }, - { - "location": "/debugging/overview/", - "text": "Debugging\n\n\nConforming your error types to \nDebuggable\n allows Vapor to create richer error messages and makes debugging easier.\n\n\nimport\n \nDebugging\n\n\n\nextension\n \nFooError\n:\n \nDebuggable\n \n{\n\n \n// conform here\n\n\n}\n\n\n\n\n\n\nNow when a \nFooError\n is thrown, you will get a nice message in your console.\n\n\nFoo Error: You \ndo\n not have a \n`\nfoo\n`\n.\n\nIdentifier: DebuggingTests.FooError.noFoo\n\nHere are some possible causes: \n- You did not \nset\n the flongwaffle.\n- The session ended before a \n`\nFoo\n`\n could be made.\n- The universe conspires against us all.\n- Computers are hard.\n\nThese suggestions could address the issue: \n- You really want to use a \n`\nBar\n`\n here.\n- Take up the guitar and move to the beach.\n\nVapor\ns documentation talks about this: \n- http://documentation.com/Foo\n- http://documentation.com/foo/noFoo", - "title": "Overview" - }, - { - "location": "/debugging/overview/#debugging", - "text": "Conforming your error types to Debuggable allows Vapor to create richer error messages and makes debugging easier. import Debugging extension FooError : Debuggable { \n // conform here } Now when a FooError is thrown, you will get a nice message in your console. Foo Error: You do not have a ` foo ` .\n\nIdentifier: DebuggingTests.FooError.noFoo\n\nHere are some possible causes: \n- You did not set the flongwaffle.\n- The session ended before a ` Foo ` could be made.\n- The universe conspires against us all.\n- Computers are hard.\n\nThese suggestions could address the issue: \n- You really want to use a ` Bar ` here.\n- Take up the guitar and move to the beach.\n\nVapor s documentation talks about this: \n- http://documentation.com/Foo\n- http://documentation.com/foo/noFoo", - "title": "Debugging" - }, - { - "location": "/deploy/cloud/", - "text": "Vapor Cloud\n\n\nThe best way to deploy your Vapor application is \nVapor Cloud\n.\n\n\n\n\nQuick Start\n\n\nIf you already have the \nVapor Toolbox\n installed, then you can deploy your\nVapor app to the cloud with just one command.\n\n\nvapor cloud deploy\n\n\n\n\n\n\n\n!!! note:\n Run the deploy command inside the root directory of your Vapor project (the one with the \nPackage.swift\n file).\n\n\nStep-by-step Guide\n\n\nVisit the \nstep-by-step guide\n on Vapor Cloud's docs for detailed\ninstructions on how to deploy your app to Vapor Cloud!", - "title": "Cloud" - }, - { - "location": "/deploy/cloud/#vapor-cloud", - "text": "The best way to deploy your Vapor application is Vapor Cloud .", - "title": "Vapor Cloud" - }, - { - "location": "/deploy/cloud/#quick-start", - "text": "If you already have the Vapor Toolbox installed, then you can deploy your\nVapor app to the cloud with just one command. vapor cloud deploy !!! note:\n Run the deploy command inside the root directory of your Vapor project (the one with the Package.swift file).", - "title": "Quick Start" - }, - { - "location": "/deploy/cloud/#step-by-step-guide", - "text": "Visit the step-by-step guide on Vapor Cloud's docs for detailed\ninstructions on how to deploy your app to Vapor Cloud!", - "title": "Step-by-step Guide" - }, - { - "location": "/deploy/nginx/", - "text": "Deploying with Nginx\n\n\nNginx 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. \n\n\n\n\nNote\n\n\nWe recommend proxying Vapor HTTP servers behind Nginx.\n\n\n\n\nOverview\n\n\nWhat 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. \n\n\nAn 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.\n\n\n\n\nMore Detail\n\n\nThe default port for receiving HTTP requests is port \n80\n (and \n443\n for HTTPS). When you bind a Vapor server to port \n80\n, 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 \n8080\n. \n\n\n\n\nNote\n\n\nPorts greater than 1024 do not require \nsudo\n to bind.\n\n\n\n\nWhen Vapor is bound to a port besides \n80\n or \n443\n, it will not be accessible to the outside internet. You then bind Nginx to port \n80\n and configure it to route requests to your Vapor server bound at port \n8080\n (or whichever port you've chosen).\n\n\nAnd that's it. If Nginx is properly configured, you will see your Vapor app responding to requests on port \n80\n. Nginx proxies the requests and responses invisibly.\n\n\nInstall Nginx\n\n\nThe 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.\n\n\nTutorials:\n\n\n\n\nHow To Install Nginx on Ubuntu 14.04 LTS\n\n\nHow To Install Nginx on Ubuntu 16.04\n\n\nHow to Deploy Nginx on Heroku\n\n\nHow To Run Nginx in a Docker Container on Ubuntu 14.04\n\n\n\n\nAPT\n\n\nNginx can be installed through APT.\n\n\nsudo apt-get update\nsudo apt-get install nginx\n\n\n\n\n\nCheck whether Nginx was installed correctly by visiting your server's IP address in a browser\n\n\nhttp://server_domain_name_or_IP\n\n\n\n\n\nService\n\n\nThe service can be started or stopped.\n\n\nsudo service nginx stop\nsudo service nginx start\nsudo service nginx restart\n\n\n\n\n\nBooting Vapor\n\n\nNginx can be started an stopped with the \nsudo service nginx ...\n commands. You will need something similar to start and stop your Vapor server.\n\n\nThere are many ways to do this, and they depend on which platform you are deploying to. Check out the \nSupervisor\n instructions to add commands for starting and stopping your Vapor app.\n\n\nConfigure Proxy\n\n\nThe configuration files for enabled sites can be found in \n/etc/nginx/sites-enabled/\n.\n\n\nCreate a new file or copy the example template from \n/etc/nginx/sites-available/\n to get started.\n\n\nHere is an example configuration file for a Vapor project called \nHello\n in the home directory.\n\n\nserver \n{\n\n server_name hello.com\n;\n\n listen \n80\n;\n\n\n root /home/vapor/Hello/Public/\n;\n\n\n location @proxy \n{\n\n proxy_pass http://127.0.0.1:8080\n;\n\n proxy_pass_header Server\n;\n\n proxy_set_header Host \n$host\n;\n\n proxy_set_header X-Real-IP \n$remote_addr\n;\n\n proxy_set_header X-Forwarded-For \n$proxy_add_x_forwarded_for\n;\n\n proxy_pass_header Server\n;\n\n proxy_connect_timeout 3s\n;\n\n proxy_read_timeout 10s\n;\n\n \n}\n\n\n}\n\n\n\n\n\n\nThis configuration file assumes the \nHello\n project binds to port \n8080\n when started in production mode.\n\n\nServing Files\n\n\nNginx 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.\n\n\nserver \n{\n\n ...\n\n \n# Serve all public/static files via nginx and then fallback to Vapor for the rest\n\n try_files \n$uri\n @proxy\n;\n\n\n location @proxy \n{\n\n ...\n \n}\n\n\n}\n\n\n\n\n\n\nTLS\n\n\nAdding TLS is relatively straightforward as long as the certificates have been properly generated. To generate TLS certificates for free, check out \nLet's Encrypt\n.\n\n\nserver \n{\n\n ...\n\n listen \n443\n ssl\n;\n\n\n ssl_certificate /etc/letsencrypt/live/hello.com/fullchain.pem\n;\n\n ssl_certificate_key /etc/letsencrypt/live/hello.com/privkey.pem\n;\n\n\n ssl_protocols TLSv1 TLSv1.1 TLSv1.2\n;\n\n ssl_prefer_server_ciphers on\n;\n\n ssl_dhparam /etc/ssl/certs/dhparam.pem\n;\n\n ssl_ciphers \nECDHE-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\n;\n\n ssl_session_timeout 1d\n;\n\n ssl_session_cache shared:SSL:50m\n;\n\n ssl_stapling on\n;\n\n ssl_stapling_verify on\n;\n\n add_header Strict-Transport-Security max-age\n=\n15768000\n;\n\n\n ...\n\n location @proxy \n{\n\n ...\n \n}\n\n\n}\n\n\n\n\n\n\nThe configuration above are the relatively strict settings for TLS with Nginx. Some of the settings here are not required, but enhance security.", - "title": "Nginx" - }, - { - "location": "/deploy/nginx/#deploying-with-nginx", - "text": "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.", - "title": "Deploying with Nginx" - }, - { - "location": "/deploy/nginx/#overview", - "text": "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.", - "title": "Overview" - }, - { - "location": "/deploy/nginx/#more-detail", - "text": "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.", - "title": "More Detail" - }, - { - "location": "/deploy/nginx/#install-nginx", - "text": "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 How To Install Nginx on Ubuntu 16.04 How to Deploy Nginx on Heroku How To Run Nginx in a Docker Container on Ubuntu 14.04", - "title": "Install Nginx" - }, - { - "location": "/deploy/nginx/#apt", - "text": "Nginx can be installed through APT. sudo apt-get update\nsudo apt-get install nginx Check whether Nginx was installed correctly by visiting your server's IP address in a browser http://server_domain_name_or_IP", - "title": "APT" - }, - { - "location": "/deploy/nginx/#service", - "text": "The service can be started or stopped. sudo service nginx stop\nsudo service nginx start\nsudo service nginx restart", - "title": "Service" - }, - { - "location": "/deploy/nginx/#booting-vapor", - "text": "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 instructions to add commands for starting and stopping your Vapor app.", - "title": "Booting Vapor" - }, - { - "location": "/deploy/nginx/#configure-proxy", - "text": "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. server { \n server_name hello.com ; \n listen 80 ; \n\n root /home/vapor/Hello/Public/ ; \n\n location @proxy { \n proxy_pass http://127.0.0.1:8080 ; \n proxy_pass_header Server ; \n proxy_set_header Host $host ; \n proxy_set_header X-Real-IP $remote_addr ; \n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ; \n proxy_pass_header Server ; \n proxy_connect_timeout 3s ; \n proxy_read_timeout 10s ; \n } } This configuration file assumes the Hello project binds to port 8080 when started in production mode.", - "title": "Configure Proxy" - }, - { - "location": "/deploy/nginx/#serving-files", - "text": "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. server { \n ...\n\n # Serve all public/static files via nginx and then fallback to Vapor for the rest \n try_files $uri @proxy ; \n\n location @proxy { \n ...\n } }", - "title": "Serving Files" - }, - { - "location": "/deploy/nginx/#tls", - "text": "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 . server { \n ...\n\n listen 443 ssl ; \n\n ssl_certificate /etc/letsencrypt/live/hello.com/fullchain.pem ; \n ssl_certificate_key /etc/letsencrypt/live/hello.com/privkey.pem ; \n\n ssl_protocols TLSv1 TLSv1.1 TLSv1.2 ; \n ssl_prefer_server_ciphers on ; \n ssl_dhparam /etc/ssl/certs/dhparam.pem ; \n 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 ; \n ssl_session_timeout 1d ; \n ssl_session_cache shared:SSL:50m ; \n ssl_stapling on ; \n ssl_stapling_verify on ; \n add_header Strict-Transport-Security max-age = 15768000 ; \n\n ...\n\n location @proxy { \n ...\n } } The configuration above are the relatively strict settings for TLS with Nginx. Some of the settings here are not required, but enhance security.", - "title": "TLS" - }, - { - "location": "/deploy/apache2/", - "text": "Deploying with Apache2\n\n\nApache2\n 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. \n\n\n\n\nNote\n\n\nThis page is for proxying behind Apache2. The recommened method is proxying Vapor HTTP servers behind \nNginx\n.\n\n\n\n\nOverview\n\n\nWhat 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. \n\n\nAn 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.\n\n\n\n\nMore Detail\n\n\nThe default port for receiving HTTP requests is port \n80\n (and \n443\n for HTTPS). When you bind a Vapor server to port \n80\n, 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 \n8080\n. \n\n\n\n\nNote\n\n\nPorts greater than 1024 do not require \nsudo\n to bind.\n\n\n\n\nWhen Vapor is bound to a port besides \n80\n or \n443\n, it will not be accessible to the outside internet. You then bind Apache2 to port \n80\n and configure it to route requests to your Vapor server bound at port \n8080\n (or whichever port you've chosen).\n\n\nAnd that's it. If Apache2 is properly configured, you will see your Vapor app responding to requests on port \n80\n. Apache2 proxies the requests and responses invisibly.\n\n\nInstall Apache2\n\n\nThe 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.\n\n\nTutorials:\n\n\n\n\nHow To Set Up Apache Virtual Hosts on Ubuntu 14.04 LTS\n\n\nHow To Set Up Apache Virtual Hosts on Ubuntu 16.04\n\n\n\n\nAPT\n\n\nApache2 can be installed through APT.\n\n\nsudo apt-get update\nsudo apt-get install apache2\n\n\n\n\n\nCheck whether Apache2 was installed correctly by visiting your server's IP address in a browser\n\n\nhttp://server_domain_name_or_IP\n\n\n\n\n\nService\n\n\nThe service can be started or stopped.\n\n\nsudo service apache2 stop\nsudo service apache2 start\nsudo service apache2 restart\n\n\n\n\n\nBooting Vapor\n\n\nApache2 can be started an stopped with the \nsudo service apache2 ...\n commands. You will need something similar to start and stop your Vapor server.\n\n\nThere are many ways to do this, and they depend on which platform you are deploying to. Check out the \nSupervisor\n and \nNginx\n instructions to add commands for starting and stopping your Vapor app.\n\n\nConfigure Proxy\n\n\nThe configuration files for enabled sites can be found in \n/etc/apache2/sites-enabled/\n.\n\n\nCreate a new file or copy the example template from \n/etc/apache2/sites-available/\n to get started.\n\n\nHere is an example configuration file for a Vapor project called \nHello\n in the home directory.\n\n\n# example.com Configuration\n\n\n\nVirtualHost\n \n*:80\n\n \nDocumentRoot\n \n/home/vapor/Hello/Public/\n\n \nServerName\n hello.com\n\n \n# Using ProxyPass will send the following headers:\n\n \n# X-Forwarded-For: The IP address of the client.\n\n \n# X-Forwarded-Host: The original host requested by the client in the Host HTTP request header.\n\n \n# X-Forwarded-Server The hostname of the proxy server.\n\n\n \nProxyPreserveHost\n \nOn\n\n \nProxyPass\n / http://127.0.0.1:8080/\n \nProxyPassReverse\n / http://127.0.0.1:8080/\n\n \nProxyTimeout\n \n3\n\n\n/VirtualHost\n \n\n\n\n\n\nThis configuration file assumes the \nHello\n project binds to port \n8080\n when started in production mode.\n\n\nServing Files\n\n\nApache2 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.\n\n\nVirtualHost\n \n*:80\n\n ...\n\n \nProxyPreserveHost\n \nOn\n\n \n# Serve all files in Public folder directly, bypassing proxy (this must be before ProxyPass /)\n\n \nProxyPass\n \n/Public\n !\n \nProxyPass\n / http://127.0.0.1:8080/\n\n ...\n\n/VirtualHost\n\n\n\n\n\n\nTLS\n\n\nAdding TLS is relatively straightforward as long as the certificates have been properly generated. To generate TLS certificates for free, check out \nLet's Encrypt\n.\n\n\nIfModule\n \nmod_ssl.c\n\n\nVirtualHost\n \n*:443\n\n ...\n\n \nSSLCertificateFile\n \n/etc/letsencrypt/live/hello.com/fullchain.pem\n\n \nSSLCertificateKeyFile\n \n/etc/letsencrypt/live/hello.com/privkey.pem\n\n \nInclude\n \n/etc/letsencrypt/options-ssl-apache.conf\n\n\n/VirtualHost\n\n\n/IfModule\n\n\n\n\n\n\nThe configuration above match the settings for TLS generated by Let's Encrypt for Apache2.", - "title": "Apache2" - }, - { - "location": "/deploy/apache2/#deploying-with-apache2", - "text": "Apache2 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 .", - "title": "Deploying with Apache2" - }, - { - "location": "/deploy/apache2/#overview", - "text": "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.", - "title": "Overview" - }, - { - "location": "/deploy/apache2/#more-detail", - "text": "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.", - "title": "More Detail" - }, - { - "location": "/deploy/apache2/#install-apache2", - "text": "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 How To Set Up Apache Virtual Hosts on Ubuntu 16.04", - "title": "Install Apache2" - }, - { - "location": "/deploy/apache2/#apt", - "text": "Apache2 can be installed through APT. sudo apt-get update\nsudo apt-get install apache2 Check whether Apache2 was installed correctly by visiting your server's IP address in a browser http://server_domain_name_or_IP", - "title": "APT" - }, - { - "location": "/deploy/apache2/#service", - "text": "The service can be started or stopped. sudo service apache2 stop\nsudo service apache2 start\nsudo service apache2 restart", - "title": "Service" - }, - { - "location": "/deploy/apache2/#booting-vapor", - "text": "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 and Nginx instructions to add commands for starting and stopping your Vapor app.", - "title": "Booting Vapor" - }, - { - "location": "/deploy/apache2/#configure-proxy", - "text": "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. # example.com Configuration VirtualHost *:80 \n DocumentRoot /home/vapor/Hello/Public/ \n ServerName hello.com\n\n # Using ProxyPass will send the following headers: \n # X-Forwarded-For: The IP address of the client. \n # X-Forwarded-Host: The original host requested by the client in the Host HTTP request header. \n # X-Forwarded-Server The hostname of the proxy server. \n\n ProxyPreserveHost On \n ProxyPass / http://127.0.0.1:8080/\n ProxyPassReverse / http://127.0.0.1:8080/\n\n ProxyTimeout 3 /VirtualHost This configuration file assumes the Hello project binds to port 8080 when started in production mode.", - "title": "Configure Proxy" - }, - { - "location": "/deploy/apache2/#serving-files", - "text": "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. VirtualHost *:80 \n ...\n\n ProxyPreserveHost On \n # Serve all files in Public folder directly, bypassing proxy (this must be before ProxyPass /) \n ProxyPass /Public !\n ProxyPass / http://127.0.0.1:8080/\n\n ... /VirtualHost", - "title": "Serving Files" - }, - { - "location": "/deploy/apache2/#tls", - "text": "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 . IfModule mod_ssl.c VirtualHost *:443 \n ...\n\n SSLCertificateFile /etc/letsencrypt/live/hello.com/fullchain.pem \n SSLCertificateKeyFile /etc/letsencrypt/live/hello.com/privkey.pem \n Include /etc/letsencrypt/options-ssl-apache.conf /VirtualHost /IfModule The configuration above match the settings for TLS generated by Let's Encrypt for Apache2.", - "title": "TLS" - }, - { - "location": "/deploy/supervisor/", - "text": "Supervisor\n\n\nSupervisor\n is a process control system that makes it easy to start, stop, and restart your Vapor app.\n\n\nInstall\n\n\nsudo apt-get update\nsudo apt-get install supervisor\n\n\n\n\n\nConfigure\n\n\nEach Vapor app on your server should have its own configuration file. For an example \nHello\n project, the configuration file would be located at \n/etc/supervisor/conf.d/hello.conf\n\n\n[\nprogram:hello\n]\n\n\ncommand\n=\n/home/vapor/hello/.build/release/Run serve --env\n=\nproduction\n\ndirectory\n=\n/home/vapor/hello/\n\nuser\n=\nwww-data\n\nstdout_logfile\n=\n/var/log/supervisor/%\n(\nprogram_name\n)\n-stdout.log\n\nstderr_logfile\n=\n/var/log/supervisor/%\n(\nprogram_name\n)\n-stderr.log\n\n\n\n\n\nAs specified in our configuration file the \nHello\n project is located in the home folder for the user \nvapor\n. Make sure \ndirectory\n points to the root directory of your project where the \nConfig/\n folder is.\n\n\nThe \n--env=production\n flag will disable verbose logging and prioritize the \nConfig/production\n sub folder of your configuration files.\n\n\nEnvironment\n\n\nYou can export variables to your Vapor app with supervisor.\n\n\nenvironment\n=\nPORT\n=\n8123\n\n\n\n\n\n\nExported variables can be used in Vapor's configuration files with the \n$\n prefix.\n\n\nConfig/production/servers.json\n\n\n{\n\n \nport\n:\n \n$PORT\n\n\n}\n\n\n\n\n\n\nThe above config file will start a server named \nmy-server\n 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.\n\n\nStart\n\n\nYou can now load and start your app.\n\n\nsupervisorctl reread\nsupervisorctl add hello\nsupervisorctl start hello\n\n\n\n\n\n\n\nNote\n\n\nThe \nadd\n command may have already started your app.", - "title": "Supervisor" - }, - { - "location": "/deploy/supervisor/#supervisor", - "text": "Supervisor is a process control system that makes it easy to start, stop, and restart your Vapor app.", - "title": "Supervisor" - }, - { - "location": "/deploy/supervisor/#install", - "text": "sudo apt-get update\nsudo apt-get install supervisor", - "title": "Install" - }, - { - "location": "/deploy/supervisor/#configure", - "text": "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 [ 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.", - "title": "Configure" - }, - { - "location": "/deploy/supervisor/#environment", - "text": "You can export variables to your Vapor app with supervisor. environment = PORT = 8123 Exported variables can be used in Vapor's configuration files with the $ prefix. Config/production/servers.json { \n 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.", - "title": "Environment" - }, - { - "location": "/deploy/supervisor/#start", - "text": "You can now load and start your app. supervisorctl reread\nsupervisorctl add hello\nsupervisorctl start hello Note The add command may have already started your app.", - "title": "Start" - }, - { - "location": "/version/1_5/", - "text": "Redirecting...", - "title": "1.5" - }, - { - "location": "/version/1_5/#redirecting", - "text": "", - "title": "Redirecting..." - }, - { - "location": "/version/2_0/", - "text": "Redirecting...", - "title": "2.0" - }, - { - "location": "/version/2_0/#redirecting", - "text": "", - "title": "Redirecting..." - }, - { - "location": "/version/3_0/", - "text": "Redirecting...", - "title": "3.0-rc" - }, - { - "location": "/version/3_0/#redirecting", - "text": "", - "title": "Redirecting..." - }, - { - "location": "/version/support/", - "text": "Version Support\n\n\n\n\nVapor 3.0 is currently in beta.\n\n\nVapor 2.0 is currently active.\n\n\nVapor 1.5 is being maintained until November 2017.\n\n\n\n\nWarning\n\n\nDashed blocks and lines represent the team's goals and are not yet guaranteed. \nVapor 3's release and the beginning of Vapor 2's maintenance phase have not yet been decided.\n\n\n\n\nCore\n\n\nAll packages in the \nVapor GitHub\n are maintained according to the following rules.\n\n\nActive\n\n\nWhile a version is active, reported security issues and bugs are fixed.\n\n\nAdditionally, new features and optimizations may be added. If new API is added, the minor version number will be incremented. At no point can existing API be removed or broken during an active version. Semver is strictly followed and tested.\n\n\nMaintenance\n\n\nWhen a new version of Vapor is released, the previous version will enter a maintenance phase which lasts for six months. During this phase, critical security issues and bugs will be fixed. No new features will be added.\n\n\n\n\nNote\n\n\nOnly the latest minor version will be maintained.\n\n\n\n\nUnstable\n\n\nThe master branch is the latest development version of Vapor and is constantly changing. Before a new version of Vapor is released, there may be unstable alpha and beta phases in which you are welcome to test and provide feedback on the changes.\n\n\nCommunity\n\n\nAll packages in the \nVapor Community GitHub\n are maintained in strict accordance of semver. Violations of semver should be reported as issues to the offending package's GitHub page.", - "title": "Support" - }, - { - "location": "/version/support/#version-support", - "text": "Vapor 3.0 is currently in beta. Vapor 2.0 is currently active. Vapor 1.5 is being maintained until November 2017. Warning Dashed blocks and lines represent the team's goals and are not yet guaranteed. \nVapor 3's release and the beginning of Vapor 2's maintenance phase have not yet been decided.", - "title": "Version Support" - }, - { - "location": "/version/support/#core", - "text": "All packages in the Vapor GitHub are maintained according to the following rules.", - "title": "Core" - }, - { - "location": "/version/support/#active", - "text": "While a version is active, reported security issues and bugs are fixed. Additionally, new features and optimizations may be added. If new API is added, the minor version number will be incremented. At no point can existing API be removed or broken during an active version. Semver is strictly followed and tested.", - "title": "Active" - }, - { - "location": "/version/support/#maintenance", - "text": "When a new version of Vapor is released, the previous version will enter a maintenance phase which lasts for six months. During this phase, critical security issues and bugs will be fixed. No new features will be added. Note Only the latest minor version will be maintained.", - "title": "Maintenance" - }, - { - "location": "/version/support/#unstable", - "text": "The master branch is the latest development version of Vapor and is constantly changing. Before a new version of Vapor is released, there may be unstable alpha and beta phases in which you are welcome to test and provide feedback on the changes.", - "title": "Unstable" - }, - { - "location": "/version/support/#community", - "text": "All packages in the Vapor Community GitHub are maintained in strict accordance of semver. Violations of semver should be reported as issues to the offending package's GitHub page.", - "title": "Community" - } - ] -} \ No newline at end of file diff --git a/build/2.0/validation/overview/index.html b/build/2.0/validation/overview/index.html deleted file mode 100644 index 9dc8e19c..00000000 --- a/build/2.0/validation/overview/index.html +++ /dev/null @@ -1,2008 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Overview - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -
-

Work in Progress

-

The subject of this page is Work in Progress and is not recommended for Production use.

-
-
-

Outdated

-

This page contains outdated information.

-
-

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.

-
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.

-
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:

-
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.

-
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.

-
let symbols = input.validated(by: !OnlyAlphanumeric.self)
-
- - -

Testing Validity

-

While validated() throw is the most common method for validating, there are two others.

-
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.

-
do {
-    //validation here
-} catch let error as ValidationErrorProtocol {
-    print(error.message)
-}
-
- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/validation/package/index.html b/build/2.0/validation/package/index.html deleted file mode 100644 index 68308715..00000000 --- a/build/2.0/validation/package/index.html +++ /dev/null @@ -1,1910 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Package - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -
-

Work in Progress

-

The subject of this page is Work in Progress and is not recommended for Production use.

-
-
-

Outdated

-

This page contains outdated information.

-
-

Using Validation

-

This section outlines how to import the Validation package both with or without a Vapor project.

-

With Vapor

-

The easiest way to use Validation with Vapor is to include the Validation provider.

-
import PackageDescription
-
-let package = Package(
-    name: "Project",
-    dependencies: [
-        .Package(url: "https://github.com/vapor/vapor.git", majorVersion: 2),
-        .Package(url: "https://github.com/vapor/validation-provider.git", majorVersion: 1)
-    ],
-    exclude: [ ... ]
-)
-
- - -

The Validation provider package adds Validation to your project and adds some additional, vapor-specific conveniences like validation middleware.

-

Using import ValidationProvider will import the Validation middleware and the Validation module.

-

Just Validation

-

At the core of the Validation provider is a Validation module.

-
import PackageDescription
-
-let package = Package(
-    name: "Project",
-    dependencies: [
-        ...
-        .Package(url: "https://github.com/vapor/validation.git", majorVersion: 1)
-    ],
-    exclude: [ ... ]
-)
-
- - -

Use import Validation to access the core validation class.

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/version/1_5/index.html b/build/2.0/version/1_5/index.html deleted file mode 100644 index 112ecac6..00000000 --- a/build/2.0/version/1_5/index.html +++ /dev/null @@ -1,1810 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1.5 - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Redirecting...

-

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/version/2_0/index.html b/build/2.0/version/2_0/index.html deleted file mode 100644 index a0708ada..00000000 --- a/build/2.0/version/2_0/index.html +++ /dev/null @@ -1,1810 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2.0 - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Redirecting...

-

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/version/3_0/index.html b/build/2.0/version/3_0/index.html deleted file mode 100644 index 46807cbc..00000000 --- a/build/2.0/version/3_0/index.html +++ /dev/null @@ -1,1810 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 3.0-rc - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Redirecting...

-

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file diff --git a/build/2.0/version/support/index.html b/build/2.0/version/support/index.html deleted file mode 100644 index 26b86679..00000000 --- a/build/2.0/version/support/index.html +++ /dev/null @@ -1,1932 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Support - Vapor Docs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Version Support

-

Support Matrix

-

Vapor 3.0 is currently in beta.

-

Vapor 2.0 is currently active.

-

Vapor 1.5 is being maintained until November 2017.

-
-

Warning

-

Dashed blocks and lines represent the team's goals and are not yet guaranteed. -Vapor 3's release and the beginning of Vapor 2's maintenance phase have not yet been decided.

-
-

Core

-

All packages in the Vapor GitHub are maintained according to the following rules.

-

Active

-

While a version is active, reported security issues and bugs are fixed.

-

Additionally, new features and optimizations may be added. If new API is added, the minor version number will be incremented. At no point can existing API be removed or broken during an active version. Semver is strictly followed and tested.

-

Maintenance

-

When a new version of Vapor is released, the previous version will enter a maintenance phase which lasts for six months. During this phase, critical security issues and bugs will be fixed. No new features will be added.

-
-

Note

-

Only the latest minor version will be maintained.

-
-

Unstable

-

The master branch is the latest development version of Vapor and is constantly changing. Before a new version of Vapor is released, there may be unstable alpha and beta phases in which you are welcome to test and provide feedback on the changes.

-

Community

-

All packages in the Vapor Community GitHub are maintained in strict accordance of semver. Violations of semver should be reported as issues to the offending package's GitHub page.

- - - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - - - \ No newline at end of file