mirror of https://github.com/vapor/docs.git
2824 lines
543 KiB
JSON
2824 lines
543 KiB
JSON
{
|
|
"docs": [
|
|
{
|
|
"location": "/",
|
|
"text": "Vapor Documentation\n\n\nThis is the documentation for Vapor, a Web Framework for Swift that works on 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 \nInstall \n macOS\n section to install Swift and Vapor.\n\n\nOnce you have Vapor installed, check out \nGetting Started \n Hello, world\n to create your first Vapor app!\n\n\nLike Vapor?\n\n\nOur small team works hard to make Vapor awesome (and free). Support the framework by \nstarring Vapor on GitHub\n\nor \ndonating $1 monthly\nit helps us a lot. Thanks!\n\n\nOther Sources\n\n\nHere are some other great places to find information about Vapor.\n\n\n\n\n\n\n\n\nname\n\n\ndescription\n\n\nlink\n\n\n\n\n\n\n\n\n\n\nVapor Slack\n\n\nChat with ~5,000 Vapor developers.\n\n\nvisit \n\n\n\n\n\n\nAPI docs\n\n\nAuto-generated documentation from code comments.\n\n\nvisit \n\n\n\n\n\n\nStack Overflow\n\n\nAsk and answer questions with the \nvapor\n tag.\n\n\nvisit \n\n\n\n\n\n\nSource Code\n\n\nLearn how Vapor works under the hood.\n\n\nvisit \n\n\n\n\n\n\nGitHub Issues\n\n\nReport bugs or request features on GitHub.\n\n\nvisit \n\n\n\n\n\n\n\n\nService Providers\n\n\nVapor providers are a convenient way to add functionality to your Vapor projects.\nFor a full list of providers, check out the \nvapor-service\n tag on GitHub.\n\n\nAuthors\n\n\nTanner Nelson\n, \nLogan Wright\n, \nJoannis Orlandos\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 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 Install macOS section to install Swift and Vapor. Once you have Vapor installed, check out Getting Started Hello, world to create your first Vapor 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 \nor 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. name description link Vapor Slack Chat with ~5,000 Vapor developers. visit API docs Auto-generated documentation from code comments. visit Stack Overflow Ask and answer questions with the vapor tag. visit Source Code Learn how Vapor works under the hood. visit GitHub Issues Report bugs or request features on GitHub. visit",
|
|
"title": "Other Sources"
|
|
},
|
|
{
|
|
"location": "/#service-providers",
|
|
"text": "Vapor providers are a convenient way to add functionality to your Vapor projects.\nFor a full list of providers, check out the vapor-service tag on GitHub.",
|
|
"title": "Service Providers"
|
|
},
|
|
{
|
|
"location": "/#authors",
|
|
"text": "Tanner Nelson , Logan Wright , Joannis Orlandos , and the hundreds of members of Vapor.",
|
|
"title": "Authors"
|
|
},
|
|
{
|
|
"location": "/install/macos/",
|
|
"text": "Install on macOS\n\n\nTo use Vapor on macOS, you just need to have Xcode 9.3 or greater installed.\n\n\nInstall Xcode\n\n\nInstall \nXcode 9.3 or greater\n from the Mac App Store.\n\n\n\n\n\n\nWarning\n\n\nAfter Xcode has been downloaded, you must open it to finish the installation. This may take a while.\n\n\n\n\nVerify Installation\n\n\nDouble check the installation was successful by opening Terminal and running:\n\n\nswift --version\n\n\n\n\n\nYou should see output similar to:\n\n\nApple Swift version \n4\n.1.0 \n(\nswiftlang-900.0.69.2 clang-900.0.38\n)\n\nTarget: x86_64-apple-macosx10.9\n\n\n\n\n\nVapor requires Swift 4.1 or greater.\n\n\nInstall Vapor\n\n\nNow that you have Swift 4.1, let's install the \nVapor Toolbox\n.\n\n\nThe toolbox includes all of Vapor's dependencies as well as a handy CLI tool for creating new projects.\n\n\nbrew install vapor/tap/vapor\n\n\n\n\n\n\n\nTip\n\n\nIf you don't already have Homebrew installed, install it at \nbrew.sh \n\n\n\n\nVerify Installation\n\n\nDouble check the installation was successful by opening Terminal and running:\n\n\nvapor --help\n\n\n\n\n\nYou should see a long list of available commands.\n\n\nDone\n\n\nNow that you have installed Vapor, create your first app in \nGetting Started \n Hello, world\n.",
|
|
"title": "macOS"
|
|
},
|
|
{
|
|
"location": "/install/macos/#install-on-macos",
|
|
"text": "To use Vapor on macOS, you just need to have Xcode 9.3 or greater installed.",
|
|
"title": "Install on macOS"
|
|
},
|
|
{
|
|
"location": "/install/macos/#install-xcode",
|
|
"text": "Install Xcode 9.3 or greater from the Mac App Store. Warning After Xcode has been downloaded, you must open it to finish the installation. This may take a while.",
|
|
"title": "Install Xcode"
|
|
},
|
|
{
|
|
"location": "/install/macos/#verify-installation",
|
|
"text": "Double check the installation was successful by opening Terminal and running: swift --version You should see output similar to: Apple Swift version 4 .1.0 ( swiftlang-900.0.69.2 clang-900.0.38 ) \nTarget: x86_64-apple-macosx10.9 Vapor requires Swift 4.1 or greater.",
|
|
"title": "Verify Installation"
|
|
},
|
|
{
|
|
"location": "/install/macos/#install-vapor",
|
|
"text": "Now that you have Swift 4.1, 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. brew install vapor/tap/vapor Tip If you don't already have Homebrew installed, install it at brew.sh",
|
|
"title": "Install Vapor"
|
|
},
|
|
{
|
|
"location": "/install/macos/#verify-installation_1",
|
|
"text": "Double check the installation was successful by opening Terminal and running: vapor --help You should see a long list of available commands.",
|
|
"title": "Verify Installation"
|
|
},
|
|
{
|
|
"location": "/install/macos/#done",
|
|
"text": "Now that you have installed Vapor, create your first app in Getting Started Hello, world .",
|
|
"title": "Done"
|
|
},
|
|
{
|
|
"location": "/install/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 Ubuntu 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\nTip\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\nRUN /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 everything worked with the following commands.\n\n\nSwift\n\n\nswift --version\n\n\n\n\n\nYou should see output similar to:\n\n\nApple Swift version \n4\n.1.0 \n(\nswiftlang-900.0.69.2 clang-900.0.38\n)\n\nTarget: x86_64-apple-macosx10.9\n\n\n\n\n\nVapor requires Swift 4.1 or greater.\n\n\nVapor Toolbox\n\n\nvapor --help\n\n\n\n\n\nYou should see a long list of available commands.\n\n\nDone\n\n\nNow that you have installed Vapor, create your first app in \nGetting Started \n Hello, world\n.\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 4.1.",
|
|
"title": "Ubuntu"
|
|
},
|
|
{
|
|
"location": "/install/ubuntu/#install-on-ubuntu",
|
|
"text": "Installing Vapor on Ubuntu only takes a couple of minutes.",
|
|
"title": "Install on Ubuntu"
|
|
},
|
|
{
|
|
"location": "/install/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": "/install/ubuntu/#apt-repo",
|
|
"text": "Add Vapor's APT repo to get access to all of Vapor's Ubuntu packages.",
|
|
"title": "APT Repo"
|
|
},
|
|
{
|
|
"location": "/install/ubuntu/#quick-script",
|
|
"text": "Easily add Vapor's APT repo with this handy script. eval $( curl -sL https://apt.vapor.sh ) Tip This command requires curl which can be installed using sudo apt-get install curl",
|
|
"title": "Quick Script"
|
|
},
|
|
{
|
|
"location": "/install/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": "/install/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": "/install/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": "/install/ubuntu/#verify-installation",
|
|
"text": "Double check everything worked with the following commands.",
|
|
"title": "Verify Installation"
|
|
},
|
|
{
|
|
"location": "/install/ubuntu/#swift",
|
|
"text": "swift --version You should see output similar to: Apple Swift version 4 .1.0 ( swiftlang-900.0.69.2 clang-900.0.38 ) \nTarget: x86_64-apple-macosx10.9 Vapor requires Swift 4.1 or greater.",
|
|
"title": "Swift"
|
|
},
|
|
{
|
|
"location": "/install/ubuntu/#vapor-toolbox",
|
|
"text": "vapor --help You should see a long list of available commands.",
|
|
"title": "Vapor Toolbox"
|
|
},
|
|
{
|
|
"location": "/install/ubuntu/#done",
|
|
"text": "Now that you have installed Vapor, create your first app in Getting Started Hello, world .",
|
|
"title": "Done"
|
|
},
|
|
{
|
|
"location": "/install/ubuntu/#swiftorg",
|
|
"text": "Check out Swift.org 's guide to using downloads if you need more detailed instructions for installing Swift 4.1.",
|
|
"title": "Swift.org"
|
|
},
|
|
{
|
|
"location": "/getting-started/hello-world/",
|
|
"text": "Hello, world\n\n\nNow that you've installed Vapor, let's create your first Vapor app!\nThis guide will take you step by step through creating a new project, building, and running it.\n\n\nNew Project\n\n\nThe first step is to create a new Vapor project on your computer.\nFor this guide, we will call the project \nHello\n.\n\n\nOpen up your terminal, and use \nVapor Toolbox's \nnew\n command.\n\n\nvapor new Hello --branch\n=\nbeta\n\n\n\n\n\n\n\nBeta\n\n\nAt the time of this writing, Vapor 3 is not officially released. You can omit the \n--branch=beta\n flag once \n3.0.0\n is released.\n\n\n\n\nOnce that finishes, change into the newly created directory.\n\n\ncd\n Hello\n\n\n\n\n\nGenerate Xcode Project\n\n\nLet's now use the \nVapor Toolbox's \nxcode\n command to generate an Xcode project.\nThis will allow us to build and run our app from inside of Xcode, just like an iOS app.\n\n\nvapor xcode\n\n\n\n\n\nThe toolbox will ask you if you'd like to open Xcode automatically, select \nyes\n.\n\n\nBuild \n Run\n\n\nYou should now have Xcode open and running. Select the \nrun scheme\n from the scheme menu,\nthen click the play button.\n\n\nYou should see the terminal pop up at the bottom of the screen.\n\n\nServer starting on http://localhost:8080\n\n\n\n\n\nVisit Localhost\n\n\nOpen your web browser, and visit \nlocalhost:8080/hello \n\n\nYou should see the following page.\n\n\nHello, world!\n\n\n\n\n\nCongratulations on creating, building, and running your first Vapor app! \ud83c\udf89",
|
|
"title": "Hello, world"
|
|
},
|
|
{
|
|
"location": "/getting-started/hello-world/#hello-world",
|
|
"text": "Now that you've installed Vapor, let's create your first Vapor app!\nThis guide will take you step by step through creating a new project, building, and running it.",
|
|
"title": "Hello, world"
|
|
},
|
|
{
|
|
"location": "/getting-started/hello-world/#new-project",
|
|
"text": "The first step is to create a new Vapor project on your computer.\nFor this guide, we will call the project Hello . Open up your terminal, and use Vapor Toolbox's new command. vapor new Hello --branch = beta Beta At the time of this writing, Vapor 3 is not officially released. You can omit the --branch=beta flag once 3.0.0 is released. Once that finishes, change into the newly created directory. cd Hello",
|
|
"title": "New Project"
|
|
},
|
|
{
|
|
"location": "/getting-started/hello-world/#generate-xcode-project",
|
|
"text": "Let's now use the Vapor Toolbox's xcode command to generate an Xcode project.\nThis will allow us to build and run our app from inside of Xcode, just like an iOS app. vapor xcode The toolbox will ask you if you'd like to open Xcode automatically, select yes .",
|
|
"title": "Generate Xcode Project"
|
|
},
|
|
{
|
|
"location": "/getting-started/hello-world/#build-run",
|
|
"text": "You should now have Xcode open and running. Select the run scheme from the scheme menu,\nthen click the play button. You should see the terminal pop up at the bottom of the screen. Server starting on http://localhost:8080",
|
|
"title": "Build & Run"
|
|
},
|
|
{
|
|
"location": "/getting-started/hello-world/#visit-localhost",
|
|
"text": "Open your web browser, and visit localhost:8080/hello You should see the following page. Hello, world! Congratulations on creating, building, and running your first Vapor app! \ud83c\udf89",
|
|
"title": "Visit Localhost"
|
|
},
|
|
{
|
|
"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\nHelp prints useful information about available commands and flags.\n\n\nvapor --help\n\n\n\n\n\nYou can also run the \n--help\n option on any Toolbox command.\n\n\nvapor new --help\n\n\n\n\n\nThe \n--help\n flag should be your goto for learning about the toolbox as it is the most up-to-date.\n\n\nNew\n\n\nThe Toolbox's most important feature is helping you create a new project.\n\n\nvapor new \nname\n\n\n\n\n\n\nJust pass the name of your project as the first argument to the \nnew\n command.\n\n\n\n\nNote\n\n\nProject names should be \nPascalCase \n, like \nHelloWorld\n or \nMyProject\n.\n\n\n\n\nTemplates\n\n\nBy default, Vapor will create your new project from the API template. You can choose\na different template by passing the \n--template\n flag.\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\n\n\nInfo\n\n\nThere are lots of unofficial Vapor templates on GitHub under the \nvapor\n + \ntemplate\n topcs \n.\nYou can use these by passing the full GitHub URL to the \n--template\n option.\n\n\n\n\nBuild \n Run\n\n\nYou can use the toolbox to build and run your Vapor app.\n\n\nvapor build\nvapor run\n\n\n\n\n\n\n\nTip\n\n\nWe recommend building and running through \nXcode\n if you have a Mac. \nIt's a bit faster and you can set breakpoints! \nJust use \nvapor xcode\n to generate an Xcode project.\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",
|
|
"title": "Toolbox"
|
|
},
|
|
{
|
|
"location": "/getting-started/toolbox/#install-toolbox",
|
|
"text": "Vapor's command line interface provides shortcuts and assistance for common tasks. Help prints useful information about available commands and flags. vapor --help You can also run the --help option on any Toolbox command. vapor new --help The --help flag should be your goto for learning about the toolbox as it is the most up-to-date.",
|
|
"title": "Install Toolbox"
|
|
},
|
|
{
|
|
"location": "/getting-started/toolbox/#new",
|
|
"text": "The Toolbox's most important feature is helping you create a new project. vapor new name Just pass the name of your project as the first argument to the new command. Note Project names should be PascalCase , like HelloWorld or MyProject .",
|
|
"title": "New"
|
|
},
|
|
{
|
|
"location": "/getting-started/toolbox/#templates",
|
|
"text": "By default, Vapor will create your new project from the API template. You can choose\na different template by passing the --template flag. Name Flag Description API --template=api JSON API with Fluent database. Web --template=web HTML website with Leaf templates. Info There are lots of unofficial Vapor templates on GitHub under the vapor + template topcs .\nYou can use these by passing the full GitHub URL to the --template option.",
|
|
"title": "Templates"
|
|
},
|
|
{
|
|
"location": "/getting-started/toolbox/#build-run",
|
|
"text": "You can use the toolbox to build and run your Vapor app. vapor build\nvapor run Tip We recommend building and running through Xcode if you have a Mac. \nIt's a bit faster and you can set breakpoints! \nJust use vapor xcode to generate an Xcode project.",
|
|
"title": "Build & Run"
|
|
},
|
|
{
|
|
"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/spm/",
|
|
"text": "Managing your project\n\n\nThe Swift Package Manager (SPM for short) is used for building your project's source code and dependencies. \nIt's a similar idea to Cocoapods, Ruby gems, and NPM. Most of the time the \nVapor Toolbox\n will \ninteract with SPM on your behalf. However, it's important to understand the basics.\n\n\n\n\nTip\n\n\nLearn more about SPM on \nSwift.org \n \n\n\n\n\nPackage Manifest\n\n\nThe first place SPM looks in your project is the package manfiest. This should always be located in the root\ndirectory of your project and named \nPackage.swift\n.\n\n\nDependencies\n\n\nDependencies are other SPM packages that your package relies on. All Vapor applications rely on the Vapor package,\nbut you can add as many other dependencies as you want.\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nVaporApp\n,\n\n \ndependencies\n:\n \n[\n\n \n// \ud83d\udca7 A server-side Swift web framework. \n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/vapor.git\n,\n \nfrom\n:\n \n3.0.0-rc\n),\n\n \n],\n\n \ntargets\n:\n \n[\n \n...\n \n]\n\n\n)\n\n\n\n\n\n\nIn the above example, you can see \nvapor/vapor \n version 3.0\nor later is a dependency of this package.\nWhen you add a dependency to your package, you must next signal which \ntargets\n depend on\nthe newly available modules.\n\n\n\n\nWarning\n\n\nAnytime you modify the package manifest, call \nvapor update\n to effect the changes.\n\n\n\n\nTargets\n\n\nTargets are all of the modules, executables, and tests that your package contains. \n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nVaporApp\n,\n\n \ndependencies\n:\n \n[\n \n...\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nApp\n,\n \ndependencies\n:\n \n[\nVapor\n]),\n\n \n.\ntarget\n(\nname\n:\n \nRun\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n.\ntestTarget\n(\nname\n:\n \nAppTests\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n]\n\n\n)\n\n\n\n\n\n\nMost Vapor apps will have three targets, although you can add as many as you like to organize your code.\nEach target declares which modules it depends on. You must add module names here in order to \nimport\n them in your code.\nA target can depend on other targets in your project or any modules exposed by packages you've added to\nthe \nmain dependencies\n array.\n\n\n\n\nTip\n\n\nExecutable targets (targets that contain a \nmain.swift\n file) cannot be imported by other modules.\nThis is why Vapor has both an \nApp\n and a \nRun\n target.\nAny code you include in \nApp\n can be tested in the \nAppTests\n.\n\n\n\n\nFolder Structure\n\n\nBelow is the typical folder structure for an SPM package.\n\n\n.\n\u251c\u2500\u2500 Sources\n\u2502 \u251c\u2500\u2500 App\n\u2502 \u2502 \u2514\u2500\u2500 (Source code)\n\u2502 \u2514\u2500\u2500 Run\n\u2502 \u2514\u2500\u2500 main.swift\n\u251c\u2500\u2500 Tests\n\u2502 \u2514\u2500\u2500 AppTests\n\u2514\u2500\u2500 Package.swift\n\n\n\n\n\nEach \n.target\n corresponds to a folder in the \nSources\n folder. \nEach \n.testTarget\n corresponds to a folder in the \nTests\n folder.\n\n\nTroubleshooting\n\n\nIf you are experiencing problems with SPM, sometimes cleaning your project can help.\n\n\nvapor clean",
|
|
"title": "SPM"
|
|
},
|
|
{
|
|
"location": "/getting-started/spm/#managing-your-project",
|
|
"text": "The Swift Package Manager (SPM for short) is used for building your project's source code and dependencies. \nIt's a similar idea to Cocoapods, Ruby gems, and NPM. Most of the time the Vapor Toolbox will \ninteract with SPM on your behalf. However, it's important to understand the basics. Tip Learn more about SPM on Swift.org",
|
|
"title": "Managing your project"
|
|
},
|
|
{
|
|
"location": "/getting-started/spm/#package-manifest",
|
|
"text": "The first place SPM looks in your project is the package manfiest. This should always be located in the root\ndirectory of your project and named Package.swift .",
|
|
"title": "Package Manifest"
|
|
},
|
|
{
|
|
"location": "/getting-started/spm/#dependencies",
|
|
"text": "Dependencies are other SPM packages that your package relies on. All Vapor applications rely on the Vapor package,\nbut you can add as many other dependencies as you want. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : VaporApp , \n dependencies : [ \n // \ud83d\udca7 A server-side Swift web framework. \n . package ( url : https://github.com/vapor/vapor.git , from : 3.0.0-rc ), \n ], \n targets : [ ... ] ) In the above example, you can see vapor/vapor version 3.0\nor later is a dependency of this package.\nWhen you add a dependency to your package, you must next signal which targets depend on\nthe newly available modules. Warning Anytime you modify the package manifest, call vapor update to effect the changes.",
|
|
"title": "Dependencies"
|
|
},
|
|
{
|
|
"location": "/getting-started/spm/#targets",
|
|
"text": "Targets are all of the modules, executables, and tests that your package contains. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : VaporApp , \n dependencies : [ ... ], \n targets : [ \n . target ( name : App , dependencies : [ Vapor ]), \n . target ( name : Run , dependencies : [ App ]), \n . testTarget ( name : AppTests , dependencies : [ App ]), \n ] ) Most Vapor apps will have three targets, although you can add as many as you like to organize your code.\nEach target declares which modules it depends on. You must add module names here in order to import them in your code.\nA target can depend on other targets in your project or any modules exposed by packages you've added to\nthe main dependencies array. Tip Executable targets (targets that contain a main.swift file) cannot be imported by other modules.\nThis is why Vapor has both an App and a Run target.\nAny code you include in App can be tested in the AppTests .",
|
|
"title": "Targets"
|
|
},
|
|
{
|
|
"location": "/getting-started/spm/#folder-structure",
|
|
"text": "Below is the typical folder structure for an SPM package. .\n\u251c\u2500\u2500 Sources\n\u2502 \u251c\u2500\u2500 App\n\u2502 \u2502 \u2514\u2500\u2500 (Source code)\n\u2502 \u2514\u2500\u2500 Run\n\u2502 \u2514\u2500\u2500 main.swift\n\u251c\u2500\u2500 Tests\n\u2502 \u2514\u2500\u2500 AppTests\n\u2514\u2500\u2500 Package.swift Each .target corresponds to a folder in the Sources folder. \nEach .testTarget corresponds to a folder in the Tests folder.",
|
|
"title": "Folder Structure"
|
|
},
|
|
{
|
|
"location": "/getting-started/spm/#troubleshooting",
|
|
"text": "If you are experiencing problems with SPM, sometimes cleaning your project can help. vapor clean",
|
|
"title": "Troubleshooting"
|
|
},
|
|
{
|
|
"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 and instruments to debug your code.\n\n\n\n\nXcode is a great way to develop Vapor apps, but you can use any text editor you like.\n\n\nGenerate Project\n\n\nTo use Xcode, you just need to generate an Xcode project using \nVapor Toolbox\n.\n\n\nvapor xcode\n\n\n\n\n\n\n\nTip\n\n\nDon't worry about comitting the generated Xcode Project to git, just generate a new\none whenever you need it.\n\n\n\n\nRun\n\n\nTo build and run your Vapor app, first make sure you have the \nRun\n scheme selected from the schemes menu.\nAlso make sure to select \"My Mac\" as the device.\n\n\n\n\nOnce that's selected, just click the play button or press \nCommand + R\n on your keyboard.\n\n\nTest\n\n\nTo run your unit tests, select the scheme ending in \n-Package\n and press \nCommand + U\n.\n\n\n\n\nWarning\n\n\nThere may be a few extraneous schemes in the dropdown menu. Ignore them!",
|
|
"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 and instruments to debug your code. Xcode is a great way to develop Vapor apps, but you can use any text editor you like.",
|
|
"title": "Xcode"
|
|
},
|
|
{
|
|
"location": "/getting-started/xcode/#generate-project",
|
|
"text": "To use Xcode, you just need to generate an Xcode project using Vapor Toolbox . vapor xcode Tip Don't worry about comitting the generated Xcode Project to git, just generate a new\none whenever you need it.",
|
|
"title": "Generate Project"
|
|
},
|
|
{
|
|
"location": "/getting-started/xcode/#run",
|
|
"text": "To build and run your Vapor app, first make sure you have the Run scheme selected from the schemes menu.\nAlso make sure to select \"My Mac\" as the device. Once that's selected, just click the play button or press Command + R on your keyboard.",
|
|
"title": "Run"
|
|
},
|
|
{
|
|
"location": "/getting-started/xcode/#test",
|
|
"text": "To run your unit tests, select the scheme ending in -Package and press Command + U . Warning There may be a few extraneous schemes in the dropdown menu. Ignore them!",
|
|
"title": "Test"
|
|
},
|
|
{
|
|
"location": "/getting-started/structure/",
|
|
"text": "Structure\n\n\nThis section explains the structure of a typical Vapor application to help get\nyou familiar with where things go.\n\n\nFolder Structure\n\n\nVapor's folder structure builds on top of \nSPM's folder structure\n.\n\n\n.\n\u251c\u2500\u2500 Public\n\u251c\u2500\u2500 Sources\n\u2502 \u251c\u2500\u2500 App\n\u2502 \u2502 \u251c\u2500\u2500 Controllers\n\u2502 \u2502 \u251c\u2500\u2500 Models\n\u2502 \u2502 \u251c\u2500\u2500 boot.swift\n\u2502 \u2502 \u251c\u2500\u2500 configure.swift\n\u2502 \u2502 \u2514\u2500\u2500 routes.swift\n\u2502 \u2514\u2500\u2500 Run\n\u2502 \u2514\u2500\u2500 main.swift\n\u251c\u2500\u2500 Tests\n\u2502 \u2514\u2500\u2500 AppTests\n\u2514\u2500\u2500 Package.swift\n\n\n\n\n\nLet's take a look at what each of these folders and files does.\n\n\nPublic\n\n\nThis folder contains any public files that will be served by your app.\nThis is usually images, style sheets, and browser scripts.\n\n\nWhenever Vapor responds to a request, it will first check if the requested\nitem is in this folder. If it is, it skips your application logic and returns\nthe file immediately.\n\n\nFor example, a request to \nlocalhost:8080/favicon.ico\n will check to see\nif \nPublic/favicon.ico\n exists. If it does, Vapor will return it.\n\n\nSources\n\n\nThis folder contains all of the Swift source files for your project. \nThe top level folders (\nApp\n and \nRun\n) reflect your package's modules, \nas declared in the \npackage manifest\n.\n\n\nApp\n\n\nThis is the most important folder in your application, it's where all of\nthe application logic goes!\n\n\nControllers\n\n\nControllers are great way of grouping together application logic. Most controllers\nhave many functions that accept a request and return some sort of response.\n\n\n\n\nTip\n\n\nVapor supports, but does not enforce the MVC pattern\n\n\n\n\nModels\n\n\nThe \nModels\n folder is a great place to store your \nContent\n structs or\nFluent \nModel\ns.\n\n\nboot.swift\n\n\nThis file contains a function that will be called \nafter\n your application has booted,\nbut \nbefore\n it has started running. This is a great place do things that should happen \nevery time your application starts.\n\n\nYou have access to the \nApplication\n here which you can use to create\nany \nservices\n you might need.\n\n\nconfigure.swift\n\n\nThis file contains a function that receives the config, environment, and services for your\napplication as input. This is a great place to make changes to your config or register \n\nservices\n to your application.\n\n\nroutes.swift\n\n\nThis file contains a function for adding routes to your router.\n\n\nYou will notice there's one example route in there that returns the \"hello, world\" response we saw earlier.\n\n\nYou can create as many methods as you want to further organize your code. Just make sure to call them in this main route collection. \n\n\nTests\n\n\nEach non-executable module in your \nSources\n folder should have a corresponding \n...Tests\n folder.\n\n\nAppTests\n\n\nThis folder contains the unit tests for code in your \nApp\n module. \nLearn more about testing in \nTesting \n Getting Started\n.\n\n\nPackage.swift\n\n\nFinally is SPM's \npackage manifest\n.",
|
|
"title": "Folder Structure"
|
|
},
|
|
{
|
|
"location": "/getting-started/structure/#structure",
|
|
"text": "This section explains the structure of a typical Vapor application to help get\nyou familiar with where things go.",
|
|
"title": "Structure"
|
|
},
|
|
{
|
|
"location": "/getting-started/structure/#folder-structure",
|
|
"text": "Vapor's folder structure builds on top of SPM's folder structure . .\n\u251c\u2500\u2500 Public\n\u251c\u2500\u2500 Sources\n\u2502 \u251c\u2500\u2500 App\n\u2502 \u2502 \u251c\u2500\u2500 Controllers\n\u2502 \u2502 \u251c\u2500\u2500 Models\n\u2502 \u2502 \u251c\u2500\u2500 boot.swift\n\u2502 \u2502 \u251c\u2500\u2500 configure.swift\n\u2502 \u2502 \u2514\u2500\u2500 routes.swift\n\u2502 \u2514\u2500\u2500 Run\n\u2502 \u2514\u2500\u2500 main.swift\n\u251c\u2500\u2500 Tests\n\u2502 \u2514\u2500\u2500 AppTests\n\u2514\u2500\u2500 Package.swift Let's take a look at what each of these folders and files does.",
|
|
"title": "Folder Structure"
|
|
},
|
|
{
|
|
"location": "/getting-started/structure/#public",
|
|
"text": "This folder contains any public files that will be served by your app.\nThis is usually images, style sheets, and browser scripts. Whenever Vapor responds to a request, it will first check if the requested\nitem is in this folder. If it is, it skips your application logic and returns\nthe file immediately. For example, a request to localhost:8080/favicon.ico will check to see\nif Public/favicon.ico exists. If it does, Vapor will return it.",
|
|
"title": "Public"
|
|
},
|
|
{
|
|
"location": "/getting-started/structure/#sources",
|
|
"text": "This folder contains all of the Swift source files for your project. \nThe top level folders ( App and Run ) reflect your package's modules, \nas declared in the package manifest .",
|
|
"title": "Sources"
|
|
},
|
|
{
|
|
"location": "/getting-started/structure/#app",
|
|
"text": "This is the most important folder in your application, it's where all of\nthe application logic goes!",
|
|
"title": "App"
|
|
},
|
|
{
|
|
"location": "/getting-started/structure/#controllers",
|
|
"text": "Controllers are great way of grouping together application logic. Most controllers\nhave many functions that accept a request and return some sort of response. Tip Vapor supports, but does not enforce the MVC pattern",
|
|
"title": "Controllers"
|
|
},
|
|
{
|
|
"location": "/getting-started/structure/#models",
|
|
"text": "The Models folder is a great place to store your Content structs or\nFluent Model s.",
|
|
"title": "Models"
|
|
},
|
|
{
|
|
"location": "/getting-started/structure/#bootswift",
|
|
"text": "This file contains a function that will be called after your application has booted,\nbut before it has started running. This is a great place do things that should happen \nevery time your application starts. You have access to the Application here which you can use to create\nany services you might need.",
|
|
"title": "boot.swift"
|
|
},
|
|
{
|
|
"location": "/getting-started/structure/#configureswift",
|
|
"text": "This file contains a function that receives the config, environment, and services for your\napplication as input. This is a great place to make changes to your config or register services to your application.",
|
|
"title": "configure.swift"
|
|
},
|
|
{
|
|
"location": "/getting-started/structure/#routesswift",
|
|
"text": "This file contains a function for adding routes to your router. You will notice there's one example route in there that returns the \"hello, world\" response we saw earlier. You can create as many methods as you want to further organize your code. Just make sure to call them in this main route collection.",
|
|
"title": "routes.swift"
|
|
},
|
|
{
|
|
"location": "/getting-started/structure/#tests",
|
|
"text": "Each non-executable module in your Sources folder should have a corresponding ...Tests folder.",
|
|
"title": "Tests"
|
|
},
|
|
{
|
|
"location": "/getting-started/structure/#apptests",
|
|
"text": "This folder contains the unit tests for code in your App module. \nLearn more about testing in Testing Getting Started .",
|
|
"title": "AppTests"
|
|
},
|
|
{
|
|
"location": "/getting-started/structure/#packageswift",
|
|
"text": "Finally is SPM's package manifest .",
|
|
"title": "Package.swift"
|
|
},
|
|
{
|
|
"location": "/getting-started/application/",
|
|
"text": "Application\n\n\nEvery Vapor project has an \nApplication\n. You use the application to run your server and create any services you might need at boot time.\n\n\nThe best place to access the application is in your project's \nboot.swift\n file.\n\n\nimport\n \nVapor\n\n\n\npublic\n \nfunc\n \nboot\n(\n_\n \napp\n:\n \nApplication\n)\n \nthrows\n \n{\n\n \n// your code here\n\n\n}\n\n\n\n\n\n\nUnlike some other web frameworks, Vapor doesn't support statically accessing the application. If you need to access it from another class or struct, you should pass through a method or initializer.\n\n\n\n\nInfo\n\n\nAvoiding static access to variables helps make Vapor performant by preventing the need for thread-safe locks or semaphores.\n\n\n\n\nServices\n\n\nThe application's main function is to boot your server. \n\n\ntry\n \napp\n.\nrun\n()\n\n\n\n\n\n\nHowever, the application is also a container. You may use it to create services required to boot your application.\n\n\n\n\nWarning\n\n\nDo not use the application, or any services created from it, inside a route closure. Use the \nRequest\n to create services instead.\n\n\n\n\nlet\n \nclient\n \n=\n \ntry\n \napp\n.\nmake\n(\nClient\n.\nself\n)\n\n\nlet\n \nres\n \n=\n \ntry\n \nclient\n.\nget\n(\nhttp://vapor.codes\n).\nwait\n()\n\n\nprint\n(\nres\n)\n \n// Response\n\n\n\n\n\n\n\n\nTip\n\n\nIt's okay to use \n.wait()\n here instead of \n.map\n/\n.flatMap\n because we are not inside of a route closure.\n\n\n\n\nLearn more about services in \nGetting Started \n Services\n.",
|
|
"title": "Application"
|
|
},
|
|
{
|
|
"location": "/getting-started/application/#application",
|
|
"text": "Every Vapor project has an Application . You use the application to run your server and create any services you might need at boot time. The best place to access the application is in your project's boot.swift file. import Vapor public func boot ( _ app : Application ) throws { \n // your code here } Unlike some other web frameworks, Vapor doesn't support statically accessing the application. If you need to access it from another class or struct, you should pass through a method or initializer. Info Avoiding static access to variables helps make Vapor performant by preventing the need for thread-safe locks or semaphores.",
|
|
"title": "Application"
|
|
},
|
|
{
|
|
"location": "/getting-started/application/#services",
|
|
"text": "The application's main function is to boot your server. try app . run () However, the application is also a container. You may use it to create services required to boot your application. Warning Do not use the application, or any services created from it, inside a route closure. Use the Request to create services instead. let client = try app . make ( Client . self ) let res = try client . get ( http://vapor.codes ). wait () print ( res ) // Response Tip It's okay to use .wait() here instead of .map / .flatMap because we are not inside of a route closure. Learn more about services in Getting Started Services .",
|
|
"title": "Services"
|
|
},
|
|
{
|
|
"location": "/getting-started/controllers/",
|
|
"text": "Controllers\n\n\nControllers are a great way to organize your code. They are collections of methods that accept a request and return a response.\n\n\nA good place to put your controllers is in the \nControllers\n folder.\n\n\nMethods\n\n\nLet's take a look at an example controller.\n\n\nimport\n \nVapor\n\n\n\nfinal\n \nclass\n \nHelloController\n \n{\n\n \nfunc\n \ngreet\n(\n_\n \nreq\n:\n \nRequest\n)\n \nthrows\n \n-\n \nString\n \n{\n\n \nreturn\n \nHello!\n\n \n}\n\n\n}\n\n\n\n\n\n\nController methods should always accept a \nRequest\n and return something \nResponseEncodable\n. \n\n\n\n\nNote\n\n\nFutures\n whose expectations are \nResponseEncodable\n (i.e, \nFuture\nString\n) are also \nResponseEncodable\n.\n\n\n\n\nTo use this controller, we can simply initialize it, then pass the method to a router.\n\n\nlet\n \nhelloController\n \n=\n \nHelloController\n()\n\n\nrouter\n.\nget\n(\ngreet\n,\n \nuse\n:\n \nhelloController\n.\ngreet\n)\n\n\n\n\n\n\nUsing Services\n\n\nYou will probably want to access your \nservices\n from within your controllers. Just use the \nRequest\n as a container to create services from within your route closures. Vapor will take care of caching the services.\n\n\nfinal\n \nclass\n \nHelloController\n \n{\n\n \nfunc\n \ngreet\n(\n_\n \nreq\n:\n \nRequest\n)\n \nthrows\n \n-\n \nString\n \n{\n\n \nreturn\n \ntry\n \nreq\n.\nmake\n(\nBCryptHasher\n.\nself\n).\nhash\n(\nhello\n)\n\n \n}\n\n\n}",
|
|
"title": "Controllers"
|
|
},
|
|
{
|
|
"location": "/getting-started/controllers/#controllers",
|
|
"text": "Controllers are a great way to organize your code. They are collections of methods that accept a request and return a response. A good place to put your controllers is in the Controllers folder.",
|
|
"title": "Controllers"
|
|
},
|
|
{
|
|
"location": "/getting-started/controllers/#methods",
|
|
"text": "Let's take a look at an example controller. import Vapor final class HelloController { \n func greet ( _ req : Request ) throws - String { \n return Hello! \n } } Controller methods should always accept a Request and return something ResponseEncodable . Note Futures whose expectations are ResponseEncodable (i.e, Future String ) are also ResponseEncodable . To use this controller, we can simply initialize it, then pass the method to a router. let helloController = HelloController () router . get ( greet , use : helloController . greet )",
|
|
"title": "Methods"
|
|
},
|
|
{
|
|
"location": "/getting-started/controllers/#using-services",
|
|
"text": "You will probably want to access your services from within your controllers. Just use the Request as a container to create services from within your route closures. Vapor will take care of caching the services. final class HelloController { \n func greet ( _ req : Request ) throws - String { \n return try req . make ( BCryptHasher . self ). hash ( hello ) \n } }",
|
|
"title": "Using Services"
|
|
},
|
|
{
|
|
"location": "/getting-started/routing/",
|
|
"text": "Routing\n\n\nRouting is the process of finding the appropriate response to an incoming request.\n\n\nMaking a Router\n\n\nIn Vapor the default Router is the \nEngineRouter\n. You can implement custom routers by implementing one conforming to the \nRouter\n protocol.\n\n\nlet\n \nrouter\n \n=\n \ntry\n \nEngineRouter\n.\ndefault\n()\n\n\n\n\n\n\nThis is usually done in your \nconfigure.swift\n file.\n\n\nRegistering a route\n\n\nImagine you want to return a list of users when someone visits \nGET /users\n. Leaving authorization aside, that would look something like this.\n\n\nrouter\n.\nget\n(\nusers\n)\n \n{\n \nreq\n \nin\n\n \nreturn\n \n// fetch the users\n\n\n}\n\n\n\n\n\n\nIn Vapor, routing is usually done using the \n.get\n, \n.put\n, \n.post\n, \n.patch\n and \n.delete\n shorthands. You can supply the path as \n/\n or comma-separated strings. We recommend comma separated, as it's more readable.\n\n\nrouter\n.\nget\n(\npath\n,\n \nto\n,\n \nsomething\n)\n \n{\n \n...\n \n}\n\n\n\n\n\n\nRoutes\n\n\nThe best place to add routes is in the \nroutes.swift\n file. Use the router supplied as a parameter to this function to register your routes.\n\n\nimport\n \nVapor\n\n\n\npublic\n \nfunc\n \nroutes\n(\n_\n \nrouter\n:\n \nRouter\n)\n \nthrows\n \n{\n\n \n// Basic \nHello, world!\n example\n\n \nrouter\n.\nget\n(\nhello\n)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nHello, world!\n\n \n}\n\n\n \n/// ...\n\n\n}\n\n\n\n\n\n\nSee \nGetting Started \n Content\n for more information about what can be returned in a route closure.\n\n\nParameters\n\n\nSometimes you may want one of the components of your route path to be dynamic. This is often used when\nyou want to get an item with a supplied identifier, e.g., \nGET /users/:id\n\n\nrouter\n.\nget\n(\nusers\n,\n \nInt\n.\nparameter\n)\n \n{\n \nreq\n \n-\n \nString\n \nin\n\n \nlet\n \nid\n \n=\n \ntry\n \nreq\n.\nparameter\n(\nInt\n.\nself\n)\n\n \nreturn\n \nrequested id #\n\\(\nid\n)\n\n\n}\n\n\n\n\n\n\nInstead of passing a string, pass the \ntype\n of parameter you expect. In this case, our \nUser\n has an \nInt\n ID.\n\n\n\n\nTip\n\n\nYou can define your own \ncustom parameter types\n as well.\n\n\n\n\nAfter registering your routes\n\n\nAfter registering your routes you must register the Router as a \nGetting Started \n Services",
|
|
"title": "Routing"
|
|
},
|
|
{
|
|
"location": "/getting-started/routing/#routing",
|
|
"text": "Routing is the process of finding the appropriate response to an incoming request.",
|
|
"title": "Routing"
|
|
},
|
|
{
|
|
"location": "/getting-started/routing/#making-a-router",
|
|
"text": "In Vapor the default Router is the EngineRouter . You can implement custom routers by implementing one conforming to the Router protocol. let router = try EngineRouter . default () This is usually done in your configure.swift file.",
|
|
"title": "Making a Router"
|
|
},
|
|
{
|
|
"location": "/getting-started/routing/#registering-a-route",
|
|
"text": "Imagine you want to return a list of users when someone visits GET /users . Leaving authorization aside, that would look something like this. router . get ( users ) { req in \n return // fetch the users } In Vapor, routing is usually done using the .get , .put , .post , .patch and .delete shorthands. You can supply the path as / or comma-separated strings. We recommend comma separated, as it's more readable. router . get ( path , to , something ) { ... }",
|
|
"title": "Registering a route"
|
|
},
|
|
{
|
|
"location": "/getting-started/routing/#routes",
|
|
"text": "The best place to add routes is in the routes.swift file. Use the router supplied as a parameter to this function to register your routes. import Vapor public func routes ( _ router : Router ) throws { \n // Basic Hello, world! example \n router . get ( hello ) { req in \n return Hello, world! \n } \n\n /// ... } See Getting Started Content for more information about what can be returned in a route closure.",
|
|
"title": "Routes"
|
|
},
|
|
{
|
|
"location": "/getting-started/routing/#parameters",
|
|
"text": "Sometimes you may want one of the components of your route path to be dynamic. This is often used when\nyou want to get an item with a supplied identifier, e.g., GET /users/:id router . get ( users , Int . parameter ) { req - String in \n let id = try req . parameter ( Int . self ) \n return requested id # \\( id ) } Instead of passing a string, pass the type of parameter you expect. In this case, our User has an Int ID. Tip You can define your own custom parameter types as well.",
|
|
"title": "Parameters"
|
|
},
|
|
{
|
|
"location": "/getting-started/routing/#after-registering-your-routes",
|
|
"text": "After registering your routes you must register the Router as a Getting Started Services",
|
|
"title": "After registering your routes"
|
|
},
|
|
{
|
|
"location": "/getting-started/content/",
|
|
"text": "Content\n\n\nIn Vapor 3, all content types (JSON, protobuf, URLEncodedForm, \nMultipart\n, etc) are treated the same. All you need to parse and serialize content is a \nCodable\n class or struct.\n\n\nFor this introduction, we will use JSON as an example. But keep in mind the API is the same for any supported content type.\n\n\nRequest\n\n\nLet's take a look at how you would parse the following HTTP request.\n\n\nPOST\n \n/login\n \nHTTP\n/\n1.1\n\n\nContent-Type\n:\n \napplication/json\n\n\n\n{\n\n \nemail\n:\n \nuser@vapor.codes\n,\n\n \npassword\n:\n \ndon\nt look!\n\n\n}\n\n\n\n\n\n\nFirst, create a struct or class that represents the data you expect.\n\n\nimport\n \nVapor\n\n\n\nstruct\n \nLoginRequest\n:\n \nContent\n \n{\n\n \nvar\n \nemail\n:\n \nString\n\n \nvar\n \npassword\n:\n \nString\n\n\n}\n\n\n\n\n\n\nThen simply conform this struct or class to \nContent\n.\nNow we are ready to decode that HTTP request.\n\n\nrouter\n.\npost\n(\nlogin\n)\n \n{\n \nreq\n \n-\n \nFuture\nHTTPStatus\n \nin\n\n \nreturn\n \nreq\n.\ncontent\n.\ndecode\n(\nLoginRequest\n.\nself\n).\nmap\n(\nto\n:\n \nHTTPStatus\n.\nself\n)\n \n{\n \nloginRequest\n \nin\n\n \nprint\n(\nloginRequest\n.\nemail\n)\n \n// user@vapor.codes\n\n \nprint\n(\nloginRequest\n.\npassword\n)\n \n// don\nt look!\n\n \nreturn\n \n.\nok\n\n \n}\n\n\n}\n\n\n\n\n\n\nWe use \n.map(to:)\n here since \nreq.content.decode(_:)\n returns a \nfuture\n.\n\n\nResponse\n\n\nLet's take a look at how you would create the following HTTP response.\n\n\nHTTP\n/\n1.1\n \n200\n \nOK\n\n\nContent-Type\n:\n \napplication/json\n\n\n\n{\n\n \nname\n:\n \nVapor User\n,\n\n \nemail\n:\n \nuser@vapor.codes\n\n\n}\n\n\n\n\n\n\nJust like decoding, first create a struct or class that represents the data that you are expecting.\n\n\nimport\n \nVapor\n\n\n\nstruct\n \nUser\n:\n \nContent\n \n{\n\n \nvar\n \nname\n:\n \nString\n\n \nvar\n \nemail\n:\n \nString\n\n\n}\n\n\n\n\n\n\nThen just conform this struct or class to \nContent\n. Now we are ready to encode that HTTP response.\n\n\nrouter\n.\nget\n(\nuser\n)\n \n{\n \nreq\n \n-\n \nUser\n \nin\n\n \nreturn\n \nUser\n(\n\n \nname\n:\n \nVapor User\n,\n\n \nemail\n:\n \nuser@vapor.codes\n\n \n)\n\n\n}\n\n\n\n\n\n\nGreat job! Now you know how to encode and decode data in Vapor. \n\n\n\n\nTip\n\n\nSee \nVapor \n Content\n for more in-depth information.\n\n\n\n\nThe next section in this guide is \nAsync\n.",
|
|
"title": "Content"
|
|
},
|
|
{
|
|
"location": "/getting-started/content/#content",
|
|
"text": "In Vapor 3, all content types (JSON, protobuf, URLEncodedForm, Multipart , etc) are treated the same. All you need to parse and serialize content is a Codable class or struct. For this introduction, we will use JSON as an example. But keep in mind the API is the same for any supported content type.",
|
|
"title": "Content"
|
|
},
|
|
{
|
|
"location": "/getting-started/content/#request",
|
|
"text": "Let's take a look at how you would parse the following HTTP request. POST /login HTTP / 1.1 Content-Type : application/json { \n email : user@vapor.codes , \n password : don t look! } First, create a struct or class that represents the data you expect. import Vapor struct LoginRequest : Content { \n var email : String \n var password : String } Then simply conform this struct or class to Content .\nNow we are ready to decode that HTTP request. router . post ( login ) { req - Future HTTPStatus in \n return req . content . decode ( LoginRequest . self ). map ( to : HTTPStatus . self ) { loginRequest in \n print ( loginRequest . email ) // user@vapor.codes \n print ( loginRequest . password ) // don t look! \n return . ok \n } } We use .map(to:) here since req.content.decode(_:) returns a future .",
|
|
"title": "Request"
|
|
},
|
|
{
|
|
"location": "/getting-started/content/#response",
|
|
"text": "Let's take a look at how you would create the following HTTP response. HTTP / 1.1 200 OK Content-Type : application/json { \n name : Vapor User , \n email : user@vapor.codes } Just like decoding, first create a struct or class that represents the data that you are expecting. import Vapor struct User : Content { \n var name : String \n var email : String } Then just conform this struct or class to Content . Now we are ready to encode that HTTP response. router . get ( user ) { req - User in \n return User ( \n name : Vapor User , \n email : user@vapor.codes \n ) } Great job! Now you know how to encode and decode data in Vapor. Tip See Vapor Content for more in-depth information. The next section in this guide is Async .",
|
|
"title": "Response"
|
|
},
|
|
{
|
|
"location": "/getting-started/async/",
|
|
"text": "Async\n\n\nYou may have noticed some APIs in Vapor expect or return a generic \nFuture\n type. If this is your first time hearing about futures, they might seem a little confusing at first. But don't worry, Vapor makes them easy to use.\n\n\nThis guide will give you a quick introduction to working with Async. Check out \nAsync \u2192 Overview\n for more information.\n\n\nFutures\n\n\nSince \nFuture\ns work asynchronously, we must use closures to interact with and transform their values. Just like optionals in Swift, futures can be mapped and flat-mapped. \n\n\nMap\n\n\nThe \n.map(to:_:)\n method allows you to transform the future's value to another value. The closure provided will be called once the \nFuture\n's data becomes available. \n\n\n/// Assume we get a future string back from some API\n\n\nlet\n \nfutureString\n:\n \nFuture\nString\n \n=\n \n...\n\n\n\n/// Map the future string to an integer\n\n\nlet\n \nfutureInt\n \n=\n \nfutureString\n.\nmap\n(\nto\n:\n \nInt\n.\nself\n)\n \n{\n \nstring\n \nin\n\n \nprint\n(\nstring\n)\n \n// The actual String\n\n \nreturn\n \nInt\n(\nstring\n)\n \n??\n \n0\n\n\n}\n\n\n\n/// We now have a future integer\n\n\nprint\n(\nfutureInt\n)\n \n// Future\nInt\n\n\n\n\n\n\nFlat Map\n\n\nThe \n.flatMap(to:_:)\n method allows you to transform the future's value to another future value. It gets the name \"flat\" map because it is what allows you to avoid creating nested futures (e.g., \nFuture\nFuture\nT\n). In other words, it helps you keep your futures flat.\n\n\n/// Assume we get a future string back from some API\n\n\nlet\n \nfutureString\n:\n \nFuture\nString\n \n=\n \n...\n\n\n\n/// Assume we have created an HTTP client\n\n\nlet\n \nclient\n:\n \nClient\n \n=\n \n...\n \n\n\n/// Flat-map the future string to a future response\n\n\nlet\n \nfutureResponse\n \n=\n \nfutureString\n.\nflatMap\n(\nto\n:\n \nResponse\n.\nself\n)\n \n{\n \nstring\n \nin\n\n \nreturn\n \nclient\n.\nget\n(\nstring\n)\n \n// Future\nResponse\n\n\n}\n\n\n\n/// We now have a future response\n\n\nprint\n(\nfutureResponse\n)\n \n// Future\nResponse\n\n\n\n\n\n\n\n\nInfo\n\n\nIf we instead used \n.map(to:_:)\n in the above example, we would have ended up with a \nFuture\nFuture\nResponse\n. Yikes!\n\n\n\n\nChaining\n\n\nThe great part about transformations on futures is that they can be chained. This allows you to express many conversions and subtasks easily.\n\n\nLet's modify the examples from above to see how we can take advantage of chaining.\n\n\n/// Assume we get a future string back from some API\n\n\nlet\n \nfutureString\n:\n \nFuture\nString\n \n=\n \n...\n\n\n\n/// Assume we have created an HTTP client\n\n\nlet\n \nclient\n:\n \nClient\n \n=\n \n...\n \n\n\n/// Transform the string to a url, then to a response\n\n\nlet\n \nfutureResponse\n \n=\n \nfutureString\n.\nmap\n(\nto\n:\n \nURL\n.\nself\n)\n \n{\n \nstring\n \nin\n\n \nguard\n \nlet\n \nurl\n \n=\n \nURL\n(\nstring\n:\n \nstring\n)\n \nelse\n \n{\n\n \nthrow\n \nAbort\n(.\nbadRequest\n,\n \nreason\n:\n \nInvalid URL string: \n\\(\nstring\n)\n)\n\n \n}\n\n \nreturn\n \nurl\n\n\n}.\nflatMap\n(\nto\n:\n \nResponse\n.\nself\n)\n \n{\n \nurl\n \nin\n\n \nreturn\n \nclient\n.\nget\n(\nurl\n)\n\n\n}\n\n\n\nprint\n(\nfutureResponse\n)\n \n// Future\nResponse\n\n\n\n\n\n\nAfter the initial call to map, there is a temporary \nFuture\nURL\n created. This future is then immediately flat-mapped to a \nFuture\nResponse\n\n\n\n\nTip\n\n\nYou can \nthrow\n errors inside of map and flat-map closures. This will result in the future failing with the error thrown.\n\n\n\n\nWorker\n\n\nYou may see methods in Vapor that have an \non: Worker\n parameter. These are usually methods that perform asynchronous work and require access to the \nEventLoop\n.\n\n\nThe most common \nWorker\ns you will interact with in Vapor are:\n\n\n\n\nApplication\n\n\nRequest\n\n\nResponse\n\n\n\n\n/// Assume we have a Request and some ViewRenderer\n\n\nlet\n \nreq\n:\n \nRequest\n \n=\n \n...\n\n\nlet\n \nview\n:\n \nViewRenderer\n \n=\n \n...\n\n\n\n/// Render the view, using the Request as a worker. \n\n\n/// This ensures the async work happens on the correct event loop.\n\n\n///\n\n\n/// This assumes the signature is:\n\n\n/// func render(_: String, on: Worker)\n\n\nview\n.\nrender\n(\nhome.html\n,\n \non\n:\n \nreq\n)",
|
|
"title": "Async"
|
|
},
|
|
{
|
|
"location": "/getting-started/async/#async",
|
|
"text": "You may have noticed some APIs in Vapor expect or return a generic Future type. If this is your first time hearing about futures, they might seem a little confusing at first. But don't worry, Vapor makes them easy to use. This guide will give you a quick introduction to working with Async. Check out Async \u2192 Overview for more information.",
|
|
"title": "Async"
|
|
},
|
|
{
|
|
"location": "/getting-started/async/#futures",
|
|
"text": "Since Future s work asynchronously, we must use closures to interact with and transform their values. Just like optionals in Swift, futures can be mapped and flat-mapped.",
|
|
"title": "Futures"
|
|
},
|
|
{
|
|
"location": "/getting-started/async/#map",
|
|
"text": "The .map(to:_:) method allows you to transform the future's value to another value. The closure provided will be called once the Future 's data becomes available. /// Assume we get a future string back from some API let futureString : Future String = ... /// Map the future string to an integer let futureInt = futureString . map ( to : Int . self ) { string in \n print ( string ) // The actual String \n return Int ( string ) ?? 0 } /// We now have a future integer print ( futureInt ) // Future Int",
|
|
"title": "Map"
|
|
},
|
|
{
|
|
"location": "/getting-started/async/#flat-map",
|
|
"text": "The .flatMap(to:_:) method allows you to transform the future's value to another future value. It gets the name \"flat\" map because it is what allows you to avoid creating nested futures (e.g., Future Future T ). In other words, it helps you keep your futures flat. /// Assume we get a future string back from some API let futureString : Future String = ... /// Assume we have created an HTTP client let client : Client = ... /// Flat-map the future string to a future response let futureResponse = futureString . flatMap ( to : Response . self ) { string in \n return client . get ( string ) // Future Response } /// We now have a future response print ( futureResponse ) // Future Response Info If we instead used .map(to:_:) in the above example, we would have ended up with a Future Future Response . Yikes!",
|
|
"title": "Flat Map"
|
|
},
|
|
{
|
|
"location": "/getting-started/async/#chaining",
|
|
"text": "The great part about transformations on futures is that they can be chained. This allows you to express many conversions and subtasks easily. Let's modify the examples from above to see how we can take advantage of chaining. /// Assume we get a future string back from some API let futureString : Future String = ... /// Assume we have created an HTTP client let client : Client = ... /// Transform the string to a url, then to a response let futureResponse = futureString . map ( to : URL . self ) { string in \n guard let url = URL ( string : string ) else { \n throw Abort (. badRequest , reason : Invalid URL string: \\( string ) ) \n } \n return url }. flatMap ( to : Response . self ) { url in \n return client . get ( url ) } print ( futureResponse ) // Future Response After the initial call to map, there is a temporary Future URL created. This future is then immediately flat-mapped to a Future Response Tip You can throw errors inside of map and flat-map closures. This will result in the future failing with the error thrown.",
|
|
"title": "Chaining"
|
|
},
|
|
{
|
|
"location": "/getting-started/async/#worker",
|
|
"text": "You may see methods in Vapor that have an on: Worker parameter. These are usually methods that perform asynchronous work and require access to the EventLoop . The most common Worker s you will interact with in Vapor are: Application Request Response /// Assume we have a Request and some ViewRenderer let req : Request = ... let view : ViewRenderer = ... /// Render the view, using the Request as a worker. /// This ensures the async work happens on the correct event loop. /// /// This assumes the signature is: /// func render(_: String, on: Worker) view . render ( home.html , on : req )",
|
|
"title": "Worker"
|
|
},
|
|
{
|
|
"location": "/getting-started/services/",
|
|
"text": "Services\n\n\nServices is a dependency injection (also called inversion of control) framework for Vapor. The services framework allows you to register, configure, and initialize anything you might need in your application.\n\n\nContainer\n\n\nMost of your interaction with services will happen through a container. A container is a combination of the following:\n\n\n\n\nServices\n: A collection of registered services.\n\n\nConfig\n: Declared preferences for certain services over others.\n\n\nEnvironment\n: The application's current environment type (testing, production, etc)\n\n\nWorker\n: The event loop associated with this container.\n\n\n\n\nThe most common containers you will interact with in Vapor are:\n\n\n\n\nApplication\n\n\nRequest\n\n\nResponse\n\n\n\n\nYou should use the \nApplication\n as a container to create services required for booting your app. You should use the \nRequest\n or \nResponse\n containers to create services for responding to requests (in route closures and controllers).\n\n\nMake\n\n\nMaking services is simple, just call \n.make(_:)\n on a container and pass the type you want, usually a protocol like \nClient\n.\n\n\nlet\n \nclient\n \n=\n \ntry\n \nreq\n.\nmake\n(\nClient\n.\nself\n)\n\n\n\n\n\n\nYou can also specify a concrete type if you know exactly what you want.\n\n\nlet\n \nleaf\n \n=\n \ntry\n \nreq\n.\nmake\n(\nLeafRenderer\n.\nself\n)\n\n\nprint\n(\nleaf\n)\n \n/// Definitely a LeafRenderer\n\n\n\nlet\n \nview\n \n=\n \ntry\n \nreq\n.\nmake\n(\nViewRenderer\n.\nself\n)\n\n\nprint\n(\nview\n)\n \n/// ViewRenderer, might be a LeafRenderer\n\n\n\n\n\n\n\n\nTip\n\n\nTry to rely on protocols over concrete types if you can. This will make testing your code easier (you can easily swap in dummy implementations) and it can help keep your code decoupled.\n\n\n\n\nServices\n\n\nThe \nServices\n struct contains all of the services you\nor the service providers you have added\nhave registered. You will usually register and configure your services in \nconfigure.swift\n.\n\n\nInstance\n\n\nYou can register initialized service instances using \n.register(_:)\n.\n\n\n/// Create an in-memory SQLite database\n\n\nlet\n \nsqlite\n \n=\n \nSQLiteDatabase\n(\nstorage\n:\n \n.\nmemory\n)\n\n\n\n/// Register to sevices.\n\n\nservices\n.\nregister\n(\nsqlite\n)\n\n\n\n\n\n\nAfter you register a service, it will be available for creation by a \nContainer\n. \n\n\nlet\n \ndb\n \n=\n \napp\n.\nmake\n(\nSQLiteDatabase\n.\nself\n)\n\n\nprint\n(\ndb\n)\n \n// SQLiteDatabase (the one we registered earlier)\n\n\n\n\n\n\nProtocol\n\n\nWhen registering services, you can also declare conformance to a particular protocol. You might have noticed that this is how Vapor registers its main router.\n\n\n/// Register routes to the router\n\n\nlet\n \nrouter\n \n=\n \nEngineRouter\n.\ndefault\n()\n\n\ntry\n \nroutes\n(\nrouter\n)\n\n\nservices\n.\nregister\n(\nrouter\n,\n \nas\n:\n \nRouter\n.\nself\n)\n\n\n\n\n\n\nSince we register the \nrouter\n variable with \nas: Router.self\n, it can be created using either the concrete type or the protocol.\n\n\nlet\n \nrouter\n \n=\n \napp\n.\nmake\n(\nRouter\n.\nself\n)\n\n\nlet\n \nengineRouter\n \n=\n \napp\n.\nmake\n(\nEngineRouter\n.\nself\n)\n\n\nprint\n(\nrouter\n)\n \n// Router (actually EngineRouter)\n\n\nprint\n(\nengineRouter\n)\n \n// EngineRouter\n\n\nprint\n(\nrouter\n \n===\n \nengineRouter\n)\n \n// true\n\n\n\n\n\n\nEnvironment\n\n\nThe environment is used to dynamically change how your Vapor app behaves in certain situations. For example, you probably want to use a different username and password for your database when your application is deployed. The \nEnvironment\n type makes managing this easy.\n\n\nWhen you run your Vapor app from the command line, you can pass an optional \n--env\n flag to specify the environment. By default, the environment will be \n.development\n.\n\n\nswift run Run --env prod\n\n\n\n\n\nIn the above example, we are running Vapor in the \n.production\n environment. This environment specifies \nisRelease = true\n.\n\n\nYou can use the environment passed into \nconfigure.swift\n to dynamically register services.\n\n\nlet\n \nsqlite\n:\n \nSQLiteDatabase\n\n\nif\n \nenv\n.\nisRelease\n \n{\n\n \n/// Create file-based SQLite db using $SQLITE_PATH from process env\n\n \nsqlite\n \n=\n \ntry\n \nSQLiteDatabase\n(\nstorage\n:\n \n.\nfile\n(\npath\n:\n \nEnvironment\n.\nget\n(\nSQLITE_PATH\n)\n!\n))\n\n\n}\n \nelse\n \n{\n\n \n/// Create an in-memory SQLite database\n\n \nsqlite\n \n=\n \ntry\n \nSQLiteDatabase\n(\nstorage\n:\n \n.\nmemory\n)\n\n\n}\n\n\nservices\n.\nregister\n(\nsqlite\n)\n\n\n\n\n\n\n\n\nInfo\n\n\nUse the static method \nEnvironment.get(_:)\n to fetch string values from the process environment.\n\n\n\n\nYou can also dynamically register services based on environment using the factory \n.register(_:)\n method.\n\n\nservices\n.\nregister\n \n{\n \ncontainer\n \n-\n \nBCryptConfig\n \nin\n\n \nlet\n \ncost\n:\n \nInt\n\n\n \nswitch\n \ncontainer\n.\nenvironment\n \n{\n\n \ncase\n \n.\nproduction\n:\n \ncost\n \n=\n \n12\n\n \ndefault\n:\n \ncost\n \n=\n \n4\n\n \n}\n\n\n \nreturn\n \nBCryptConfig\n(\ncost\n:\n \ncost\n)\n\n\n}\n\n\n\n\n\n\nConfig\n\n\nIf multiple services are available for a given protocol, you will need to use the \nConfig\n struct to declare which service you prefer.\n\n\nServiceError.ambiguity: Please choose which KeyedCache you prefer, multiple are available: MemoryKeyedCache, FluentCache\nSQLiteDatabase\n.\n\n\n\n\n\nThis is also done in \nconfigure.swift\n, just use the \nconfig.prefer(_:for:)\n method.\n\n\n/// Declare preference for MemoryKeyedCache anytime a container is asked to create a KeyedCache\n\n\nconfig\n.\nprefer\n(\nMemoryKeyedCache\n.\nself\n,\n \nfor\n:\n \nKeyedCache\n.\nself\n)\n\n\n\n/// ...\n\n\n\n/// Create a KeyedCache using the Request container\n\n\nlet\n \ncache\n \n=\n \nreq\n.\nmake\n(\nKeyedCache\n.\nself\n)\n\n\nprint\n(\ncache\n \nis\n \nMemoryKeyedCache\n)\n \n// true",
|
|
"title": "Services"
|
|
},
|
|
{
|
|
"location": "/getting-started/services/#services",
|
|
"text": "Services is a dependency injection (also called inversion of control) framework for Vapor. The services framework allows you to register, configure, and initialize anything you might need in your application.",
|
|
"title": "Services"
|
|
},
|
|
{
|
|
"location": "/getting-started/services/#container",
|
|
"text": "Most of your interaction with services will happen through a container. A container is a combination of the following: Services : A collection of registered services. Config : Declared preferences for certain services over others. Environment : The application's current environment type (testing, production, etc) Worker : The event loop associated with this container. The most common containers you will interact with in Vapor are: Application Request Response You should use the Application as a container to create services required for booting your app. You should use the Request or Response containers to create services for responding to requests (in route closures and controllers).",
|
|
"title": "Container"
|
|
},
|
|
{
|
|
"location": "/getting-started/services/#make",
|
|
"text": "Making services is simple, just call .make(_:) on a container and pass the type you want, usually a protocol like Client . let client = try req . make ( Client . self ) You can also specify a concrete type if you know exactly what you want. let leaf = try req . make ( LeafRenderer . self ) print ( leaf ) /// Definitely a LeafRenderer let view = try req . make ( ViewRenderer . self ) print ( view ) /// ViewRenderer, might be a LeafRenderer Tip Try to rely on protocols over concrete types if you can. This will make testing your code easier (you can easily swap in dummy implementations) and it can help keep your code decoupled.",
|
|
"title": "Make"
|
|
},
|
|
{
|
|
"location": "/getting-started/services/#services_1",
|
|
"text": "The Services struct contains all of the services you or the service providers you have added have registered. You will usually register and configure your services in configure.swift .",
|
|
"title": "Services"
|
|
},
|
|
{
|
|
"location": "/getting-started/services/#instance",
|
|
"text": "You can register initialized service instances using .register(_:) . /// Create an in-memory SQLite database let sqlite = SQLiteDatabase ( storage : . memory ) /// Register to sevices. services . register ( sqlite ) After you register a service, it will be available for creation by a Container . let db = app . make ( SQLiteDatabase . self ) print ( db ) // SQLiteDatabase (the one we registered earlier)",
|
|
"title": "Instance"
|
|
},
|
|
{
|
|
"location": "/getting-started/services/#protocol",
|
|
"text": "When registering services, you can also declare conformance to a particular protocol. You might have noticed that this is how Vapor registers its main router. /// Register routes to the router let router = EngineRouter . default () try routes ( router ) services . register ( router , as : Router . self ) Since we register the router variable with as: Router.self , it can be created using either the concrete type or the protocol. let router = app . make ( Router . self ) let engineRouter = app . make ( EngineRouter . self ) print ( router ) // Router (actually EngineRouter) print ( engineRouter ) // EngineRouter print ( router === engineRouter ) // true",
|
|
"title": "Protocol"
|
|
},
|
|
{
|
|
"location": "/getting-started/services/#environment",
|
|
"text": "The environment is used to dynamically change how your Vapor app behaves in certain situations. For example, you probably want to use a different username and password for your database when your application is deployed. The Environment type makes managing this easy. When you run your Vapor app from the command line, you can pass an optional --env flag to specify the environment. By default, the environment will be .development . swift run Run --env prod In the above example, we are running Vapor in the .production environment. This environment specifies isRelease = true . You can use the environment passed into configure.swift to dynamically register services. let sqlite : SQLiteDatabase if env . isRelease { \n /// Create file-based SQLite db using $SQLITE_PATH from process env \n sqlite = try SQLiteDatabase ( storage : . file ( path : Environment . get ( SQLITE_PATH ) ! )) } else { \n /// Create an in-memory SQLite database \n sqlite = try SQLiteDatabase ( storage : . memory ) } services . register ( sqlite ) Info Use the static method Environment.get(_:) to fetch string values from the process environment. You can also dynamically register services based on environment using the factory .register(_:) method. services . register { container - BCryptConfig in \n let cost : Int \n\n switch container . environment { \n case . production : cost = 12 \n default : cost = 4 \n } \n\n return BCryptConfig ( cost : cost ) }",
|
|
"title": "Environment"
|
|
},
|
|
{
|
|
"location": "/getting-started/services/#config",
|
|
"text": "If multiple services are available for a given protocol, you will need to use the Config struct to declare which service you prefer. ServiceError.ambiguity: Please choose which KeyedCache you prefer, multiple are available: MemoryKeyedCache, FluentCache SQLiteDatabase . This is also done in configure.swift , just use the config.prefer(_:for:) method. /// Declare preference for MemoryKeyedCache anytime a container is asked to create a KeyedCache config . prefer ( MemoryKeyedCache . self , for : KeyedCache . self ) /// ... /// Create a KeyedCache using the Request container let cache = req . make ( KeyedCache . self ) print ( cache is MemoryKeyedCache ) // true",
|
|
"title": "Config"
|
|
},
|
|
{
|
|
"location": "/getting-started/cloud/",
|
|
"text": "Deployment\n\n\nDeploying code is the process of making your Vapor project publically available. \nIt can be one of the most difficult aspects of web development. Fortunately, there\nare services to help.\n\n\nVapor Cloud\n\n\nThe best way to deploy your application is through Vapor Cloud. It's a cloud platform built\nspecifically for the Vapor web framework. This means it's incredibly easy to deploy your\nproject quickly and be confident that it will be fast and stable.\n\n\nDeploying your project to Vapor Cloud is simple, it's built right into the \nVapor Toolbox\n.\nJust run this command from within the root directory of your project.\n\n\nvapor cloud deploy\n\n\n\n\n\nFor a detailed guide, visit \nVapor Cloud \n Quick Start\n.\n\n\nOther Options\n\n\nVapor can be deployed anywhere that supports Ubuntu (basically everywhere). Guides on deploying to other systems are coming soon (contributions welcome)!",
|
|
"title": "Deployment"
|
|
},
|
|
{
|
|
"location": "/getting-started/cloud/#deployment",
|
|
"text": "Deploying code is the process of making your Vapor project publically available. \nIt can be one of the most difficult aspects of web development. Fortunately, there\nare services to help.",
|
|
"title": "Deployment"
|
|
},
|
|
{
|
|
"location": "/getting-started/cloud/#vapor-cloud",
|
|
"text": "The best way to deploy your application is through Vapor Cloud. It's a cloud platform built\nspecifically for the Vapor web framework. This means it's incredibly easy to deploy your\nproject quickly and be confident that it will be fast and stable. Deploying your project to Vapor Cloud is simple, it's built right into the Vapor Toolbox .\nJust run this command from within the root directory of your project. vapor cloud deploy For a detailed guide, visit Vapor Cloud Quick Start .",
|
|
"title": "Vapor Cloud"
|
|
},
|
|
{
|
|
"location": "/getting-started/cloud/#other-options",
|
|
"text": "Vapor can be deployed anywhere that supports Ubuntu (basically everywhere). Guides on deploying to other systems are coming soon (contributions welcome)!",
|
|
"title": "Other Options"
|
|
},
|
|
{
|
|
"location": "/async/getting-started/",
|
|
"text": "Getting Started with Async\n\n\nThe Async module is provided as a part of Vapor Core (\nvapor/core\n). It is a collection of convenience APIs (mostly extensions) built on top of \nSwiftNIO\n.\n\n\n\n\nTip\n\n\nYou can read more about SwiftNIO's async types (\nFuture\n, \nPromise\n, \nEventLoop\n, and more) in its GitHub \nREADME\n or its \nAPI Docs\n.\n\n\n\n\nUsage\n\n\nThis package is included with Vapor and exported by default. You will have access to all \nAsync\n APIs when you import \nVapor\n.\n\n\nimport\n \nVapor\n \n// implies `import Async`\n\n\n\n\n\n\nStandalone\n\n\nThe Async module, part of the larger Vapor Core package, can also be used on its own with any Swift project.\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\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 \nfrom\n:\n \n3.0.0\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nAsync\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Async\n to access the APIs.\n\n\nOverview\n\n\nContinue to \nAsync \n Overview\n for an overview of Async's features.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/async/getting-started/#getting-started-with-async",
|
|
"text": "The Async module is provided as a part of Vapor Core ( vapor/core ). It is a collection of convenience APIs (mostly extensions) built on top of SwiftNIO . Tip You can read more about SwiftNIO's async types ( Future , Promise , EventLoop , and more) in its GitHub README or its API Docs .",
|
|
"title": "Getting Started with Async"
|
|
},
|
|
{
|
|
"location": "/async/getting-started/#usage",
|
|
"text": "This package is included with Vapor and exported by default. You will have access to all Async APIs when you import Vapor . import Vapor // implies `import Async`",
|
|
"title": "Usage"
|
|
},
|
|
{
|
|
"location": "/async/getting-started/#standalone",
|
|
"text": "The Async module, part of the larger Vapor Core package, can also be used on its own with any Swift project. To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . package ( url : https://github.com/vapor/core.git , from : 3.0.0 ), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ Async , ... ]) \n ] ) Use import Async to access the APIs.",
|
|
"title": "Standalone"
|
|
},
|
|
{
|
|
"location": "/async/getting-started/#overview",
|
|
"text": "Continue to Async Overview for an overview of Async's features.",
|
|
"title": "Overview"
|
|
},
|
|
{
|
|
"location": "/async/overview/",
|
|
"text": "Async Overview\n\n\nYou may have noticed some APIs in Vapor expect or return a generic \nFuture\n type. If this is your first time hearing about futures, they might seem a little confusing at first. But don't worry, Vapor makes them easy to use.\n\n\nPromises and futures are related, but distinct, types. Promises are used to \ncreate\n futures. Most of the time, you will be working with futures returned by Vapor's APIs and you will not need to worry about creating promises.\n\n\n\n\n\n\n\n\ntype\n\n\ndescription\n\n\nmutability\n\n\nmethods\n\n\n\n\n\n\n\n\n\n\nFuture\n\n\nReference to an object that may not be available yet.\n\n\nread-only\n\n\n.map(to:_:)\n \n.flatMap(to:_:)\n \ndo(_:)\n \ncatch(_:)\n\n\n\n\n\n\nPromise\n\n\nA promise to provide some object asynchronously.\n\n\nread/write\n\n\nsucceed(_:)\n \nfail(_:)\n\n\n\n\n\n\n\n\nFutures are an alternative to callback-based asynchronous APIs. Futures can be chained and transformed in ways that simple closures cannot, making them quite powerful.\n\n\nTransforming\n\n\nJust like optionals in Swift, futures can be mapped and flat-mapped. These are the most common operations you will perform on futures.\n\n\n\n\n\n\n\n\nmethod\n\n\nsignature\n\n\ndescription\n\n\n\n\n\n\n\n\n\n\nmap\n\n\nto: U.Type, _: (T) -\n U\n\n\nMaps a future value to a different value.\n\n\n\n\n\n\nflatMap\n\n\nto: U.Type, _: (T) -\n Future\nU\n\n\nMaps a future value to different \nfuture\n value.\n\n\n\n\n\n\ntransform\n\n\nto: U\n\n\nMaps a future to an already available value.\n\n\n\n\n\n\n\n\nIf you look at the method signatures for \nmap\n and \nflatMap\n on \nOptional\nT\n and \nArray\nT\n, you will see that they are very similar to the methods available on \nFuture\nT\n.\n\n\nMap\n\n\nThe \n.map(to:_:)\n method allows you to transform the future's value to another value. Because the future's value may not be available yet (it may be the result of an asynchronous task) we must provide a closure to accept the value.\n\n\n/// Assume we get a future string back from some API\n\n\nlet\n \nfutureString\n:\n \nFuture\nString\n \n=\n \n...\n\n\n\n/// Map the future string to an integer\n\n\nlet\n \nfutureInt\n \n=\n \nfutureString\n.\nmap\n(\nto\n:\n \nInt\n.\nself\n)\n \n{\n \nstring\n \nin\n\n \nprint\n(\nstring\n)\n \n// The actual String\n\n \nreturn\n \nInt\n(\nstring\n)\n \n??\n \n0\n\n\n}\n\n\n\n/// We now have a future integer\n\n\nprint\n(\nfutureInt\n)\n \n// Future\nInt\n\n\n\n\n\n\nFlat Map\n\n\nThe \n.flatMap(to:_:)\n method allows you to transform the future's value to another future value. It gets the name \"flat\" map because it is what allows you to avoid creating nested futures (e.g., \nFuture\nFuture\nT\n). In other words, it helps you keep your generic futures flat.\n\n\n/// Assume we get a future string back from some API\n\n\nlet\n \nfutureString\n:\n \nFuture\nString\n \n=\n \n...\n\n\n\n/// Assume we have created an HTTP client\n\n\nlet\n \nclient\n:\n \nClient\n \n=\n \n...\n \n\n\n/// Flat-map the future string to a future response\n\n\nlet\n \nfutureResponse\n \n=\n \nfutureString\n.\nflatMap\n(\nto\n:\n \nResponse\n.\nself\n)\n \n{\n \nstring\n \nin\n\n \nreturn\n \nclient\n.\nget\n(\nstring\n)\n \n// Future\nResponse\n\n\n}\n\n\n\n/// We now have a future response\n\n\nprint\n(\nfutureResponse\n)\n \n// Future\nResponse\n\n\n\n\n\n\n\n\nInfo\n\n\nIf we instead used \n.map(to:_:)\n in the above example, we would have ended up with a \nFuture\nFuture\nResponse\n. Yikes!\n\n\n\n\nTransform\n\n\nThe \n.transform(_:)\n method allows you to modify a future's value, ignoring the existing value. This is especially useful for transforming the results of \nFuture\nVoid\n where the actual value of the future is not important.\n\n\n\n\nTip\n\n\nFuture\nVoid\n, sometimes called a signal, is a future whose sole purpose is to notify you of completion or failure of some async operation.\n\n\n\n\n/// Assume we get a void future back from some API\n\n\nlet\n \nuserDidSave\n:\n \nFuture\nVoid\n \n=\n \n...\n\n\n\n/// Transform the void future to an HTTP status\n\n\nlet\n \nfutureStatus\n \n=\n \nuserDidSave\n.\ntransform\n(\nto\n:\n \nHTTPStatus\n.\nok\n)\n\n\nprint\n(\nfutureStatus\n)\n \n// Future\nHTTPStatus\n\n\n\n\n\n\nEven though we have supplied an already-available value to \ntransform\n, this is still a \ntransformation\n. The future will not complete until all previous futures have completed (or failed).\n\n\nChaining\n\n\nThe great part about transformations on futures is that they can be chained. This allows you to express many conversions and subtasks easily.\n\n\nLet's modify the examples from above to see how we can take advantage of chaining.\n\n\n/// Assume we get a future string back from some API\n\n\nlet\n \nfutureString\n:\n \nFuture\nString\n \n=\n \n...\n\n\n\n/// Assume we have created an HTTP client\n\n\nlet\n \nclient\n:\n \nClient\n \n=\n \n...\n \n\n\n/// Transform the string to a url, then to a response\n\n\nlet\n \nfutureResponse\n \n=\n \nfutureString\n.\nmap\n(\nto\n:\n \nURL\n.\nself\n)\n \n{\n \nstring\n \nin\n\n \nguard\n \nlet\n \nurl\n \n=\n \nURL\n(\nstring\n:\n \nstring\n)\n \nelse\n \n{\n\n \nthrow\n \nAbort\n(.\nbadRequest\n,\n \nreason\n:\n \nInvalid URL string: \n\\(\nstring\n)\n)\n\n \n}\n\n \nreturn\n \nurl\n\n\n}.\nflatMap\n(\nto\n:\n \nResponse\n.\nself\n)\n \n{\n \nurl\n \nin\n\n \nreturn\n \nclient\n.\nget\n(\nurl\n)\n\n\n}\n\n\n\nprint\n(\nfutureResponse\n)\n \n// Future\nResponse\n\n\n\n\n\n\nAfter the initial call to map, there is a temporary \nFuture\nURL\n created. This future is then immediately flat-mapped to a \nFuture\nResponse\n\n\n\n\nTip\n\n\nYou can \nthrow\n errors inside of map and flat-map closures. This will result in the future failing with the error thrown.\n\n\n\n\nFuture\n\n\nLet's take a look at some other, less commonly used methods on \nFuture\nT\n.\n\n\nDo / Catch\n\n\nSimilar to Swift's \ndo\n / \ncatch\n syntax, futures have a \ndo\n and \ncatch\n method for awaiting the future's result.\n\n\n/// Assume we get a future string back from some API\n\n\nlet\n \nfutureString\n:\n \nFuture\nString\n \n=\n \n...\n\n\n\nfutureString\n.\ndo\n \n{\n \nstring\n \nin\n\n \nprint\n(\nstring\n)\n \n// The actual String\n\n\n}.\ncatch\n \n{\n \nerror\n \nin\n\n \nprint\n(\nerror\n)\n \n// A Swift Error\n\n\n}\n\n\n\n\n\n\n\n\nInfo\n\n\n.do\n and \n.catch\n work together. If you forget \n.catch\n, the compiler will warn you about an unused result. Don't forget to handle the error case!\n\n\n\n\nAlways\n\n\nYou can use \nalways\n to add a callback that will be executed whether the future succeeds or fails.\n\n\n/// Assume we get a future string back from some API\n\n\nlet\n \nfutureString\n:\n \nFuture\nString\n \n=\n \n...\n\n\n\nfutureString\n.\nalways\n \n{\n\n \nprint\n(\nThe future is complete!\n)\n\n\n}\n\n\n\n\n\n\n\n\nNote\n\n\nYou can add as many callbacks to a future as you want.\n\n\n\n\nWait\n\n\nYou can use \n.wait()\n to synchronously wait for the future to be completed. Since a future may fail, this call is throwing.\n\n\n/// Assume we get a future string back from some API\n\n\nlet\n \nfutureString\n:\n \nFuture\nString\n \n=\n \n...\n\n\n\n/// Block until the string is ready\n\n\nlet\n \nstring\n \n=\n \ntry\n \nfutureString\n.\nwait\n()\n\n\nprint\n(\nstring\n)\n \n/// String\n\n\n\n\n\n\n\n\nWarning\n\n\nDo not use this method in route closures or controllers. Read the section about \nBlocking\n for more information.\n\n\n\n\nPromise\n\n\nMost of the time, you will be transforming futures returned by calls to Vapor's APIs. However, at some point you may need to create a promise of your own.\n\n\nTo create a promise, you will need access to an \nEventLoop\n. All containers in Vapor have an \neventLoop\n property that you can use. Most commonly, this will be the current \nRequest\n.\n\n\n/// Create a new promise for some string\n\n\nlet\n \npromiseString\n \n=\n \nreq\n.\neventLoop\n.\nnewPromise\n(\nString\n.\nself\n)\n\n\nprint\n(\npromiseString\n)\n \n// Promise\nString\n\n\nprint\n(\npromiseString\n.\nfutureResult\n)\n \n// Future\nString\n\n\n\n/// Completes the associated future\n\n\npromiseString\n.\nsucceed\n(\nresult\n:\n \nHello\n)\n\n\n\n/// Fails the associated future\n\n\npromiseString\n.\nfail\n(\nerror\n:\n \n...)\n\n\n\n\n\n\n\n\nInfo\n\n\nA promise can only be completed once. Any subsequent completions will be ignored.\n\n\n\n\nThread Safety\n\n\nPromises can be completed (\nsucceed(result:)\n / \nfail(error:)\n) from any thread. This is why promises require an event-loop to be initialized. Promises ensure that the completion action gets returned to its event-loop for execution.\n\n\nEvent Loop\n\n\nWhen your application boots, it will usually create one event loop for each core in the CPU it is running on. Each event loop has exactly one thread. If you are familiar with event loops from Node.js, the ones in Vapor are very similar. The only difference is that Vapor can run multiple event loops in one process since Swift supports multi-threading.\n\n\nEach time a client connects to your server, it will be assigned to one of the event loops. From that point on, all communication between the server and that client will happen on that same event loop (and by association, that event loop's thread). \n\n\nThe event loop is responsible for keeping track of each connected client's state. If there is a request from the client waiting to be read, the event loop trigger a read notification, causing the data to be read. Once the entire request is read, any futures waiting for that request's data will be completed. \n\n\nWorker\n\n\nThings that have access to an event loop are called \nWorkers\n. Every container in Vapor is a worker. \n\n\nThe most common containers you will interact with in Vapor are:\n\n\n\n\nApplication\n\n\nRequest\n\n\nResponse\n\n\n\n\nYou can use the \n.eventLoop\n property on these containers to gain access to the event loop.\n\n\nprint\n(\napp\n.\neventLoop\n)\n \n// EventLoop\n\n\n\n\n\n\nThere are many methods in Vapor that require the current worker to be passed along. It will usually be labeled like \non: Worker\n. If you are in a route closure or a controller, pass the current \nRequest\n or \nResponse\n. If you need a worker while booting your app, use the \nApplication\n.\n\n\nBlocking\n\n\nAn absolutely critical rule is the following:\n\n\n\n\nDanger\n\n\nNever make blocking calls directly on an event loop.\n\n\n\n\nAn example of a blocking call would be something like \nlibc.sleep(_:)\n.\n\n\nrouter\n.\nget\n(\nhello\n)\n \n{\n \nreq\n \nin\n\n \n/// Puts the event loop\ns thread to sleep.\n\n \nsleep\n(\n5\n)\n\n\n \n/// Returns a simple string once the thread re-awakens.\n\n \nreturn\n \nHello, world!\n\n\n}\n\n\n\n\n\n\nsleep(_:)\n is a command that blocks the current thread for the number of seconds supplied. If you do blocking work directly on an event loop, the event loop will be unable to respond to any other clients assigned to it for the duration of the blocking work. In other words, if you do \nsleep(5)\n on an event loop, all of the other clients connected to that event loop (possibly hundreds or thousands) will be delayed for at least 5 seconds.\n\n\nMake sure to run any blocking work in the background. Use promises to notify the event loop when this work is done in a non-blocking way.\n\n\nrouter\n.\nget\n(\nhello\n)\n \n{\n \nreq\n \nin\n\n \n/// Create a new void promise\n\n \nlet\n \npromise\n \n=\n \nreq\n.\neventLoop\n.\nnewPromise\n(\nVoid\n.\nself\n)\n\n\n \n/// Dispatch some work to happen on a background thread\n\n \nDispatchQueue\n.\nglobal\n()\n \n{\n\n \n/// Puts the background thread to sleep\n\n \n/// This will not affect any of the event loops\n\n \nsleep\n(\n5\n)\n\n\n \n/// When the \nblocking work\n has completed,\n\n \n/// complete the promise and its associated future.\n\n \npromise\n.\nsucceed\n()\n\n \n}\n\n\n \n/// Wait for the future to be completed, \n\n \n/// then transform the result to a simple String\n\n \nreturn\n \npromise\n.\nfutureResult\n.\ntransform\n(\nto\n:\n \nHello, world!\n)\n\n\n}\n\n\n\n\n\n\nNot all blocking calls will be as obvious as \nsleep(_:)\n. If you are suspicious that a call you are using may be blocking, research the method itself or ask someone. Chances are if the function is doing disk or network IO and uses a synchronous API (no callbacks or futures) it is blocking.\n\n\n\n\nInfo\n\n\nIf doing blocking work is a central part of your application, you should consider using a \nBlockingIOThreadPool\n to control the number of threads you create to do blocking work. This will help you avoid starving your event loops from CPU time while blocking work is being done.",
|
|
"title": "Overview"
|
|
},
|
|
{
|
|
"location": "/async/overview/#async-overview",
|
|
"text": "You may have noticed some APIs in Vapor expect or return a generic Future type. If this is your first time hearing about futures, they might seem a little confusing at first. But don't worry, Vapor makes them easy to use. Promises and futures are related, but distinct, types. Promises are used to create futures. Most of the time, you will be working with futures returned by Vapor's APIs and you will not need to worry about creating promises. type description mutability methods Future Reference to an object that may not be available yet. read-only .map(to:_:) .flatMap(to:_:) do(_:) catch(_:) Promise A promise to provide some object asynchronously. read/write succeed(_:) fail(_:) Futures are an alternative to callback-based asynchronous APIs. Futures can be chained and transformed in ways that simple closures cannot, making them quite powerful.",
|
|
"title": "Async Overview"
|
|
},
|
|
{
|
|
"location": "/async/overview/#transforming",
|
|
"text": "Just like optionals in Swift, futures can be mapped and flat-mapped. These are the most common operations you will perform on futures. method signature description map to: U.Type, _: (T) - U Maps a future value to a different value. flatMap to: U.Type, _: (T) - Future U Maps a future value to different future value. transform to: U Maps a future to an already available value. If you look at the method signatures for map and flatMap on Optional T and Array T , you will see that they are very similar to the methods available on Future T .",
|
|
"title": "Transforming"
|
|
},
|
|
{
|
|
"location": "/async/overview/#map",
|
|
"text": "The .map(to:_:) method allows you to transform the future's value to another value. Because the future's value may not be available yet (it may be the result of an asynchronous task) we must provide a closure to accept the value. /// Assume we get a future string back from some API let futureString : Future String = ... /// Map the future string to an integer let futureInt = futureString . map ( to : Int . self ) { string in \n print ( string ) // The actual String \n return Int ( string ) ?? 0 } /// We now have a future integer print ( futureInt ) // Future Int",
|
|
"title": "Map"
|
|
},
|
|
{
|
|
"location": "/async/overview/#flat-map",
|
|
"text": "The .flatMap(to:_:) method allows you to transform the future's value to another future value. It gets the name \"flat\" map because it is what allows you to avoid creating nested futures (e.g., Future Future T ). In other words, it helps you keep your generic futures flat. /// Assume we get a future string back from some API let futureString : Future String = ... /// Assume we have created an HTTP client let client : Client = ... /// Flat-map the future string to a future response let futureResponse = futureString . flatMap ( to : Response . self ) { string in \n return client . get ( string ) // Future Response } /// We now have a future response print ( futureResponse ) // Future Response Info If we instead used .map(to:_:) in the above example, we would have ended up with a Future Future Response . Yikes!",
|
|
"title": "Flat Map"
|
|
},
|
|
{
|
|
"location": "/async/overview/#transform",
|
|
"text": "The .transform(_:) method allows you to modify a future's value, ignoring the existing value. This is especially useful for transforming the results of Future Void where the actual value of the future is not important. Tip Future Void , sometimes called a signal, is a future whose sole purpose is to notify you of completion or failure of some async operation. /// Assume we get a void future back from some API let userDidSave : Future Void = ... /// Transform the void future to an HTTP status let futureStatus = userDidSave . transform ( to : HTTPStatus . ok ) print ( futureStatus ) // Future HTTPStatus Even though we have supplied an already-available value to transform , this is still a transformation . The future will not complete until all previous futures have completed (or failed).",
|
|
"title": "Transform"
|
|
},
|
|
{
|
|
"location": "/async/overview/#chaining",
|
|
"text": "The great part about transformations on futures is that they can be chained. This allows you to express many conversions and subtasks easily. Let's modify the examples from above to see how we can take advantage of chaining. /// Assume we get a future string back from some API let futureString : Future String = ... /// Assume we have created an HTTP client let client : Client = ... /// Transform the string to a url, then to a response let futureResponse = futureString . map ( to : URL . self ) { string in \n guard let url = URL ( string : string ) else { \n throw Abort (. badRequest , reason : Invalid URL string: \\( string ) ) \n } \n return url }. flatMap ( to : Response . self ) { url in \n return client . get ( url ) } print ( futureResponse ) // Future Response After the initial call to map, there is a temporary Future URL created. This future is then immediately flat-mapped to a Future Response Tip You can throw errors inside of map and flat-map closures. This will result in the future failing with the error thrown.",
|
|
"title": "Chaining"
|
|
},
|
|
{
|
|
"location": "/async/overview/#future",
|
|
"text": "Let's take a look at some other, less commonly used methods on Future T .",
|
|
"title": "Future"
|
|
},
|
|
{
|
|
"location": "/async/overview/#do-catch",
|
|
"text": "Similar to Swift's do / catch syntax, futures have a do and catch method for awaiting the future's result. /// Assume we get a future string back from some API let futureString : Future String = ... futureString . do { string in \n print ( string ) // The actual String }. catch { error in \n print ( error ) // A Swift Error } Info .do and .catch work together. If you forget .catch , the compiler will warn you about an unused result. Don't forget to handle the error case!",
|
|
"title": "Do / Catch"
|
|
},
|
|
{
|
|
"location": "/async/overview/#always",
|
|
"text": "You can use always to add a callback that will be executed whether the future succeeds or fails. /// Assume we get a future string back from some API let futureString : Future String = ... futureString . always { \n print ( The future is complete! ) } Note You can add as many callbacks to a future as you want.",
|
|
"title": "Always"
|
|
},
|
|
{
|
|
"location": "/async/overview/#wait",
|
|
"text": "You can use .wait() to synchronously wait for the future to be completed. Since a future may fail, this call is throwing. /// Assume we get a future string back from some API let futureString : Future String = ... /// Block until the string is ready let string = try futureString . wait () print ( string ) /// String Warning Do not use this method in route closures or controllers. Read the section about Blocking for more information.",
|
|
"title": "Wait"
|
|
},
|
|
{
|
|
"location": "/async/overview/#promise",
|
|
"text": "Most of the time, you will be transforming futures returned by calls to Vapor's APIs. However, at some point you may need to create a promise of your own. To create a promise, you will need access to an EventLoop . All containers in Vapor have an eventLoop property that you can use. Most commonly, this will be the current Request . /// Create a new promise for some string let promiseString = req . eventLoop . newPromise ( String . self ) print ( promiseString ) // Promise String print ( promiseString . futureResult ) // Future String /// Completes the associated future promiseString . succeed ( result : Hello ) /// Fails the associated future promiseString . fail ( error : ...) Info A promise can only be completed once. Any subsequent completions will be ignored.",
|
|
"title": "Promise"
|
|
},
|
|
{
|
|
"location": "/async/overview/#thread-safety",
|
|
"text": "Promises can be completed ( succeed(result:) / fail(error:) ) from any thread. This is why promises require an event-loop to be initialized. Promises ensure that the completion action gets returned to its event-loop for execution.",
|
|
"title": "Thread Safety"
|
|
},
|
|
{
|
|
"location": "/async/overview/#event-loop",
|
|
"text": "When your application boots, it will usually create one event loop for each core in the CPU it is running on. Each event loop has exactly one thread. If you are familiar with event loops from Node.js, the ones in Vapor are very similar. The only difference is that Vapor can run multiple event loops in one process since Swift supports multi-threading. Each time a client connects to your server, it will be assigned to one of the event loops. From that point on, all communication between the server and that client will happen on that same event loop (and by association, that event loop's thread). The event loop is responsible for keeping track of each connected client's state. If there is a request from the client waiting to be read, the event loop trigger a read notification, causing the data to be read. Once the entire request is read, any futures waiting for that request's data will be completed.",
|
|
"title": "Event Loop"
|
|
},
|
|
{
|
|
"location": "/async/overview/#worker",
|
|
"text": "Things that have access to an event loop are called Workers . Every container in Vapor is a worker. The most common containers you will interact with in Vapor are: Application Request Response You can use the .eventLoop property on these containers to gain access to the event loop. print ( app . eventLoop ) // EventLoop There are many methods in Vapor that require the current worker to be passed along. It will usually be labeled like on: Worker . If you are in a route closure or a controller, pass the current Request or Response . If you need a worker while booting your app, use the Application .",
|
|
"title": "Worker"
|
|
},
|
|
{
|
|
"location": "/async/overview/#blocking",
|
|
"text": "An absolutely critical rule is the following: Danger Never make blocking calls directly on an event loop. An example of a blocking call would be something like libc.sleep(_:) . router . get ( hello ) { req in \n /// Puts the event loop s thread to sleep. \n sleep ( 5 ) \n\n /// Returns a simple string once the thread re-awakens. \n return Hello, world! } sleep(_:) is a command that blocks the current thread for the number of seconds supplied. If you do blocking work directly on an event loop, the event loop will be unable to respond to any other clients assigned to it for the duration of the blocking work. In other words, if you do sleep(5) on an event loop, all of the other clients connected to that event loop (possibly hundreds or thousands) will be delayed for at least 5 seconds. Make sure to run any blocking work in the background. Use promises to notify the event loop when this work is done in a non-blocking way. router . get ( hello ) { req in \n /// Create a new void promise \n let promise = req . eventLoop . newPromise ( Void . self ) \n\n /// Dispatch some work to happen on a background thread \n DispatchQueue . global () { \n /// Puts the background thread to sleep \n /// This will not affect any of the event loops \n sleep ( 5 ) \n\n /// When the blocking work has completed, \n /// complete the promise and its associated future. \n promise . succeed () \n } \n\n /// Wait for the future to be completed, \n /// then transform the result to a simple String \n return promise . futureResult . transform ( to : Hello, world! ) } Not all blocking calls will be as obvious as sleep(_:) . If you are suspicious that a call you are using may be blocking, research the method itself or ask someone. Chances are if the function is doing disk or network IO and uses a synchronous API (no callbacks or futures) it is blocking. Info If doing blocking work is a central part of your application, you should consider using a BlockingIOThreadPool to control the number of threads you create to do blocking work. This will help you avoid starving your event loops from CPU time while blocking work is being done.",
|
|
"title": "Blocking"
|
|
},
|
|
{
|
|
"location": "/console/getting-started/",
|
|
"text": "Getting Started with Console\n\n\nThe Console module is provided as a part of Vapor's Console package (\nvapor/console\n). This module provides APIs for performing console I/O including things like outputting stylized text, requesting user input, and displaying activity indicators like loading bars.\n\n\n\n\nTip\n\n\nFor an in-depth look at all of Console's APIs, check out the \nConsole API docs\n.\n\n\n\n\nUsage\n\n\nThis package is included with Vapor and exported by default. You will have access to all \nConsole\n APIs when you import \nVapor\n.\n\n\nimport\n \nVapor\n \n// implies import Console\n\n\n\n\n\n\nStandalone\n\n\nThe Console module, part of the larger Vapor Console package, can also be used on its own with any Swift project.\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\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/// \ud83d\udcbb APIs for creating interactive CLI tools.\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/console.git\n,\n \nfrom\n:\n \n3.0.0\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nConsole\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Console\n to access the APIs.\n\n\nOverview\n\n\nContinue to \nConsole \u2192 Overview\n for an overview of Console's features.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/console/getting-started/#getting-started-with-console",
|
|
"text": "The Console module is provided as a part of Vapor's Console package ( vapor/console ). This module provides APIs for performing console I/O including things like outputting stylized text, requesting user input, and displaying activity indicators like loading bars. Tip For an in-depth look at all of Console's APIs, check out the Console API docs .",
|
|
"title": "Getting Started with Console"
|
|
},
|
|
{
|
|
"location": "/console/getting-started/#usage",
|
|
"text": "This package is included with Vapor and exported by default. You will have access to all Console APIs when you import Vapor . import Vapor // implies import Console",
|
|
"title": "Usage"
|
|
},
|
|
{
|
|
"location": "/console/getting-started/#standalone",
|
|
"text": "The Console module, part of the larger Vapor Console package, can also be used on its own with any Swift project. To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n /// \ud83d\udcbb APIs for creating interactive CLI tools. \n . package ( url : https://github.com/vapor/console.git , from : 3.0.0 ), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ Console , ... ]) \n ] ) Use import Console to access the APIs.",
|
|
"title": "Standalone"
|
|
},
|
|
{
|
|
"location": "/console/getting-started/#overview",
|
|
"text": "Continue to Console \u2192 Overview for an overview of Console's features.",
|
|
"title": "Overview"
|
|
},
|
|
{
|
|
"location": "/console/overview/",
|
|
"text": "Console Overview\n\n\nThis guide will give you a brief introduction to the Console module, showing you how to output stylized text and request user input.\n\n\nTerminal\n\n\nA default implementation of the \nConsole\n protocol called \nTerminal\n is provided for you to use.\n\n\nlet\n \nterminal\n \n=\n \nTerminal\n()\n\n\nprint\n(\nterminal\n \nis\n \nConsole\n)\n \n// true\n\n\nterminal\n.\nprint\n(\nHello\n)\n\n\n\n\n\n\nThe rest of this guide will assume a generic \nConsole\n, but using \nTerminal\n directly will also work fine. You can use any available \nContainer\n to create a console.\n\n\nlet\n \nconsole\n \n=\n \ntry\n \nreq\n.\nmake\n(\nConsole\n.\nself\n)\n\n\nconsole\n.\nprint\n(\nHello\n)\n\n\n\n\n\n\nOutput\n\n\nConsole\n provides several convenience methods for outputting strings, like \nprint(_:)\n and \nwarning(_:)\n. All of these methods eventually call \noutput(_:)\n which is the most powerful output method. This method accepts \nConsoleText\n which supports independently styled string components.\n\n\n/// Prints \nHello, world\n, but the word \nworld\n is blue.\n\n\nconsole\n.\noutput\n(\nHello, \n \n+\n \nworld\n.\nconsoleText\n(\ncolor\n:\n \n.\nblue\n))\n\n\n\n\n\n\nYou can combine as many differently styled fragments to a \nConsoleText\n as you like. All \nConsole\n methods that output text should have an overload for accepting \nConsoleText\n.\n\n\nInput\n\n\nConsole\n offers several methods for requesting input from the user, the most basic of which is \ninput(isSecure:)\n.\n\n\n/// Accepts input from the terminal until the first newline.\n\n\nlet\n \ninput\n \n=\n \nconsole\n.\ninput\n()\n\n\nconsole\n.\nprint\n(\nYou wrote: \n\\(\ninput\n)\n)\n\n\n\n\n\n\nAsk\n\n\nUse \nask(_:)\n to supply a prompt and input indicator to the user.\n\n\n/// Outputs the prompt then requests input.\n\n\nlet\n \nname\n \n=\n \nconsole\n.\nask\n(\nWhat is your name?\n)\n\n\nconsole\n.\nprint\n(\nYou said: \n\\(\nname\n)\n)\n\n\n\n\n\n\nThe above code will output:\n\n\nWhat is your name?\n\n Vapor\nYou said: Vapor\n\n\n\n\n\nConfirm\n\n\nUse \nconfirm(_:)\n to prompt the user for yes / no input.\n\n\n/// Prompts the user for yes / no input.\n\n\nif\n \nconsole\n.\nconfirm\n(\nAre you sure?\n)\n \n{\n\n \n// they are sure\n\n\n}\n \nelse\n \n{\n\n \n// don\nt do it!\n\n\n}\n\n\n\n\n\n\nThe above code will output:\n\n\nAre\n \nyou\n \nsure\n?\n\n\ny\n/\nn\n \nyes\n\n\n\n\n\n\n\n\nNote\n\n\nconfirm(_:)\n will continue to prompt the user until they respond with something recognized as yes or no.",
|
|
"title": "Overview"
|
|
},
|
|
{
|
|
"location": "/console/overview/#console-overview",
|
|
"text": "This guide will give you a brief introduction to the Console module, showing you how to output stylized text and request user input.",
|
|
"title": "Console Overview"
|
|
},
|
|
{
|
|
"location": "/console/overview/#terminal",
|
|
"text": "A default implementation of the Console protocol called Terminal is provided for you to use. let terminal = Terminal () print ( terminal is Console ) // true terminal . print ( Hello ) The rest of this guide will assume a generic Console , but using Terminal directly will also work fine. You can use any available Container to create a console. let console = try req . make ( Console . self ) console . print ( Hello )",
|
|
"title": "Terminal"
|
|
},
|
|
{
|
|
"location": "/console/overview/#output",
|
|
"text": "Console provides several convenience methods for outputting strings, like print(_:) and warning(_:) . All of these methods eventually call output(_:) which is the most powerful output method. This method accepts ConsoleText which supports independently styled string components. /// Prints Hello, world , but the word world is blue. console . output ( Hello, + world . consoleText ( color : . blue )) You can combine as many differently styled fragments to a ConsoleText as you like. All Console methods that output text should have an overload for accepting ConsoleText .",
|
|
"title": "Output"
|
|
},
|
|
{
|
|
"location": "/console/overview/#input",
|
|
"text": "Console offers several methods for requesting input from the user, the most basic of which is input(isSecure:) . /// Accepts input from the terminal until the first newline. let input = console . input () console . print ( You wrote: \\( input ) )",
|
|
"title": "Input"
|
|
},
|
|
{
|
|
"location": "/console/overview/#ask",
|
|
"text": "Use ask(_:) to supply a prompt and input indicator to the user. /// Outputs the prompt then requests input. let name = console . ask ( What is your name? ) console . print ( You said: \\( name ) ) The above code will output: What is your name? Vapor\nYou said: Vapor",
|
|
"title": "Ask"
|
|
},
|
|
{
|
|
"location": "/console/overview/#confirm",
|
|
"text": "Use confirm(_:) to prompt the user for yes / no input. /// Prompts the user for yes / no input. if console . confirm ( Are you sure? ) { \n // they are sure } else { \n // don t do it! } The above code will output: Are you sure ? y / n yes Note confirm(_:) will continue to prompt the user until they respond with something recognized as yes or no.",
|
|
"title": "Confirm"
|
|
},
|
|
{
|
|
"location": "/command/getting-started/",
|
|
"text": "Getting Started with Command\n\n\nThe Command module is provided as a part of Vapor's Console package (\nvapor/console\n). This module provides APIs for creating command-line interfaces (CLIs). It's what powers the \nVapor Toolbox\n.\n\n\n\n\nTip\n\n\nFor an in-depth look at all of Command's APIs, check out the \nCommand API docs\n.\n\n\n\n\nUsage\n\n\nThis package is included with Vapor and exported by default. You will have access to all \nCommand\n APIs when you import \nVapor\n.\n\n\nimport\n \nVapor\n \n// implies import Command\n\n\n\n\n\n\nStandalone\n\n\nThe Command module, part of the larger Vapor Console package, can also be used on its own with any Swift project.\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\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/// \ud83d\udcbb APIs for creating interactive CLI tools.\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/console.git\n,\n \nfrom\n:\n \n3.0.0\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nCommand\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Command\n to access the APIs.\n\n\nOverview\n\n\nContinue to \nCommand \u2192 Overview\n for an overview of Command's features.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/command/getting-started/#getting-started-with-command",
|
|
"text": "The Command module is provided as a part of Vapor's Console package ( vapor/console ). This module provides APIs for creating command-line interfaces (CLIs). It's what powers the Vapor Toolbox . Tip For an in-depth look at all of Command's APIs, check out the Command API docs .",
|
|
"title": "Getting Started with Command"
|
|
},
|
|
{
|
|
"location": "/command/getting-started/#usage",
|
|
"text": "This package is included with Vapor and exported by default. You will have access to all Command APIs when you import Vapor . import Vapor // implies import Command",
|
|
"title": "Usage"
|
|
},
|
|
{
|
|
"location": "/command/getting-started/#standalone",
|
|
"text": "The Command module, part of the larger Vapor Console package, can also be used on its own with any Swift project. To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n /// \ud83d\udcbb APIs for creating interactive CLI tools. \n . package ( url : https://github.com/vapor/console.git , from : 3.0.0 ), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ Command , ... ]) \n ] ) Use import Command to access the APIs.",
|
|
"title": "Standalone"
|
|
},
|
|
{
|
|
"location": "/command/getting-started/#overview",
|
|
"text": "Continue to Command \u2192 Overview for an overview of Command's features.",
|
|
"title": "Overview"
|
|
},
|
|
{
|
|
"location": "/command/overview/",
|
|
"text": "Command Overview\n\n\nThis guide will introduce you to the Command module by showing you how to create your own CLI. For this example, we will implement \ncowsay\n, a command that prints an ASCII picture of a cow with a message.\n\n\n\n\nTip\n\n\nYou can install the real \ncowsay\n program using \nbrew install cowsay\n.\n\n\n\n\n$ cowsay Hello\n -----\n\n Hello \n\n -----\n \n\\ \n ^__^\n \n\\ \n \n(\noo\n\\_\n______\n \n(\n__\n)\n\\ \n \n)\n\\/\\\n\n \n||\n----w \n|\n\n \n||\n \n||\n\n\n\n\n\n\nCommand\n\n\nThe first step is to create a type that conforms to \nCommand\n.\n\n\n/// Generates ASCII picture of a cow with a message.\n\n\nstruct\n \nCowsayCommand\n:\n \nCommand\n \n{\n\n \n...\n\n\n}\n\n\n\n\n\n\nNow let's implement the required methods.\n\n\nArguments\n\n\nCommands can have zero or more \nCommandArgument\ns. These arguments will be required for the command to run.\n\n\n/// Generates ASCII picture of a cow with a message.\n\n\nstruct\n \nCowsayCommand\n:\n \nCommand\n \n{\n\n \n/// See `Command`\n\n \nvar\n \narguments\n:\n \n[\nCommandArgument\n]\n \n{\n\n \nreturn\n \n[.\nargument\n(\nname\n:\n \nmessage\n)]\n\n \n}\n\n\n \n...\n\n\n}\n\n\n\n\n\n\nHere we are defining one argument, the \nmessage\n that the cow will say. This is required to run the \ncowsay\n command.\n\n\nOptions\n\n\nCommands can have zero or more \nCommandOption\ns. These options are not required for the command to run and can be passed using \n--\n or \n-\n syntax.\n\n\n/// Generates ASCII picture of a cow with a message.\n\n\nstruct\n \nCowsayCommand\n:\n \nCommand\n \n{\n\n \n...\n\n \n/// See `Command`\n\n \nvar\n \noptions\n:\n \n[\nCommandOption\n]\n \n{\n\n \nreturn\n \n[\n\n \n.\nvalue\n(\nname\n:\n \neyes\n,\n \nshort\n:\n \ne\n,\n \ndefault\n:\n \noo\n,\n \nhelp\n:\n \n[\nChange cow\ns eyes\n]),\n\n \n.\nvalue\n(\nname\n:\n \ntongue\n,\n \nshort\n:\n \nt\n,\n \ndefault\n:\n \n \n,\n \nhelp\n:\n \n[\nChange cow\ns tongue\n]),\n\n \n]\n\n \n}\n\n \n...\n\n\n}\n\n\n\n\n\n\nHere we are defining two options, \neyes\n and \ntongue\n. These will let the user optionally change how the cow looks.\n\n\nHelp\n\n\nNext we can define an optional help message to display when the user passes \n--help\n.\n\n\n/// Generates ASCII picture of a cow with a message.\n\n\nstruct\n \nCowsayCommand\n:\n \nCommand\n \n{\n\n \n...\n\n \n/// See `Command`\n\n \nvar\n \nhelp\n:\n \n[\nString\n]\n \n{\n\n \nreturn\n \n[\nGenerates ASCII picture of a cow with a message.\n]\n\n \n}\n\n \n...\n\n\n}\n\n\n\n\n\n\nLet's take a look at how this will look once our command is complete:\n\n\nUsage: \nexecutable\n cowsay \nmessage\n \n[\n--eyes,-e\n]\n \n[\n--tongue,-t\n]\n \n\nGenerates ASCII picture of a cow with a message.\n\nArguments:\n message n/a\n\nOptions:\n eyes Change cow\ns eyes\n\n\n tongue Change cow\ns tongue\n\n\n\n\n\nRun\n\n\nFinally, we need to write our implementation:\n\n\n/// Generates ASCII picture of a cow with a message.\n\n\nstruct\n \nCowsayCommand\n:\n \nCommand\n \n{\n\n \n...\n\n\n \n/// See `Command`.\n\n \nfunc\n \nrun\n(\nusing\n \ncontext\n:\n \nCommandContext\n)\n \nthrows\n \n-\n \nFuture\n \n{\n\n \nlet\n \nmessage\n \n=\n \ntry\n \ncontext\n.\nargument\n(\nmessage\n)\n\n \n/// We can use requireOption here since both options have default values\n\n \nlet\n \neyes\n \n=\n \ntry\n \ncontext\n.\nrequireOption\n(\neyes\n)\n\n \nlet\n \ntongue\n \n=\n \ntry\n \ncontext\n.\nrequireOption\n(\ntongue\n)\n\n \nlet\n \npadding\n \n=\n \nString\n(\nrepeating\n:\n \n-\n,\n \ncount\n:\n \nmessage\n.\ncount\n)\n\n \nlet\n \ntext\n:\n \nString\n \n=\n \n\n\n \n\\(\npadding\n)\n\n\n \n \n\\(\nmessage\n)\n \n\n\n \n\\(\npadding\n)\n\n\n \n\\\\\n ^__^\n\n\n \n\\\\\n (\n\\(\neyes\n)\n\\\\\n_______\n\n\n (__)\n\\\\\n )\n\\\\\n/\n\\\\\n\n\n \n\\(\ntongue\n)\n ||----w |\n\n\n || ||\n\n\n \n\n \ncontext\n.\nconsole\n.\nprint\n(\ntext\n)\n\n \nreturn\n \n.\ndone\n(\non\n:\n \ncontext\n.\ncontainer\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nThe \nCommandContext\n gives you access to everything you will need, including a \nContainer\n. Now that we have a complete \nCommand\n, the next step is to configure it.\n\n\nConfig\n\n\nUse the \nCommandConfig\n struct to register commands to your container. This is usually done in \nconfigure.swift\n\n\n/// Create a `CommandConfig` with default commands.\n\n\nvar\n \ncommandConfig\n \n=\n \nCommandConfig\n.\ndefault\n()\n\n\n/// Add the `CowsayCommand`.\n\n\ncommandConfig\n.\nuse\n(\nCowsayCommand\n(),\n \nas\n:\n \ncowsay\n)\n\n\n/// Register this `CommandConfig` to services.\n\n\nservices\n.\nregister\n(\ncommandConfig\n)\n\n\n\n\n\n\nCheck that your command was properly configured using \n--help\n.\n\n\nswift\n \nrun\n \nRun\n \ncowsay\n \n--\nhelp\n\n\n\n\n\n\nThat's it!\n\n\n$\n \nswift\n \nrun\n \nRun\n \ncowsay\n \nGood\n \njob\n!\n \n-\ne\n \n^^\n \n-\nt\n \nU\n\n \n---------\n\n\n \nGood\n \njob\n!\n \n\n \n---------\n\n \n\\\n \n^\n__\n^\n\n \n\\\n \n(\n^^\n\\\n_______\n\n \n(\n__\n)\n\\\n \n)\n\\\n/\n\\\n\n \nU\n \n||----\nw\n \n|\n\n \n||\n \n||",
|
|
"title": "Overview"
|
|
},
|
|
{
|
|
"location": "/command/overview/#command-overview",
|
|
"text": "This guide will introduce you to the Command module by showing you how to create your own CLI. For this example, we will implement cowsay , a command that prints an ASCII picture of a cow with a message. Tip You can install the real cowsay program using brew install cowsay . $ cowsay Hello\n ----- Hello \n -----\n \\ ^__^\n \\ ( oo \\_ ______\n ( __ ) \\ ) \\/\\ \n || ----w | \n || ||",
|
|
"title": "Command Overview"
|
|
},
|
|
{
|
|
"location": "/command/overview/#command",
|
|
"text": "The first step is to create a type that conforms to Command . /// Generates ASCII picture of a cow with a message. struct CowsayCommand : Command { \n ... } Now let's implement the required methods.",
|
|
"title": "Command"
|
|
},
|
|
{
|
|
"location": "/command/overview/#arguments",
|
|
"text": "Commands can have zero or more CommandArgument s. These arguments will be required for the command to run. /// Generates ASCII picture of a cow with a message. struct CowsayCommand : Command { \n /// See `Command` \n var arguments : [ CommandArgument ] { \n return [. argument ( name : message )] \n } \n\n ... } Here we are defining one argument, the message that the cow will say. This is required to run the cowsay command.",
|
|
"title": "Arguments"
|
|
},
|
|
{
|
|
"location": "/command/overview/#options",
|
|
"text": "Commands can have zero or more CommandOption s. These options are not required for the command to run and can be passed using -- or - syntax. /// Generates ASCII picture of a cow with a message. struct CowsayCommand : Command { \n ... \n /// See `Command` \n var options : [ CommandOption ] { \n return [ \n . value ( name : eyes , short : e , default : oo , help : [ Change cow s eyes ]), \n . value ( name : tongue , short : t , default : , help : [ Change cow s tongue ]), \n ] \n } \n ... } Here we are defining two options, eyes and tongue . These will let the user optionally change how the cow looks.",
|
|
"title": "Options"
|
|
},
|
|
{
|
|
"location": "/command/overview/#help",
|
|
"text": "Next we can define an optional help message to display when the user passes --help . /// Generates ASCII picture of a cow with a message. struct CowsayCommand : Command { \n ... \n /// See `Command` \n var help : [ String ] { \n return [ Generates ASCII picture of a cow with a message. ] \n } \n ... } Let's take a look at how this will look once our command is complete: Usage: executable cowsay message [ --eyes,-e ] [ --tongue,-t ] \n\nGenerates ASCII picture of a cow with a message.\n\nArguments:\n message n/a\n\nOptions:\n eyes Change cow s eyes tongue Change cow s tongue",
|
|
"title": "Help"
|
|
},
|
|
{
|
|
"location": "/command/overview/#run",
|
|
"text": "Finally, we need to write our implementation: /// Generates ASCII picture of a cow with a message. struct CowsayCommand : Command { \n ... \n\n /// See `Command`. \n func run ( using context : CommandContext ) throws - Future { \n let message = try context . argument ( message ) \n /// We can use requireOption here since both options have default values \n let eyes = try context . requireOption ( eyes ) \n let tongue = try context . requireOption ( tongue ) \n let padding = String ( repeating : - , count : message . count ) \n let text : String = \\( padding ) \\( message ) \\( padding ) \\\\ ^__^ \\\\ ( \\( eyes ) \\\\ _______ (__) \\\\ ) \\\\ / \\\\ \\( tongue ) ||----w | || || \n context . console . print ( text ) \n return . done ( on : context . container ) \n } } The CommandContext gives you access to everything you will need, including a Container . Now that we have a complete Command , the next step is to configure it.",
|
|
"title": "Run"
|
|
},
|
|
{
|
|
"location": "/command/overview/#config",
|
|
"text": "Use the CommandConfig struct to register commands to your container. This is usually done in configure.swift /// Create a `CommandConfig` with default commands. var commandConfig = CommandConfig . default () /// Add the `CowsayCommand`. commandConfig . use ( CowsayCommand (), as : cowsay ) /// Register this `CommandConfig` to services. services . register ( commandConfig ) Check that your command was properly configured using --help . swift run Run cowsay -- help That's it! $ swift run Run cowsay Good job ! - e ^^ - t U \n --------- Good job ! \n --------- \n \\ ^ __ ^ \n \\ ( ^^ \\ _______ \n ( __ ) \\ ) \\ / \\ \n U ||---- w | \n || ||",
|
|
"title": "Config"
|
|
},
|
|
{
|
|
"location": "/core/getting-started/",
|
|
"text": "Getting Started with Async\n\n\nComing soon.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/core/getting-started/#getting-started-with-async",
|
|
"text": "Coming soon.",
|
|
"title": "Getting Started with Async"
|
|
},
|
|
{
|
|
"location": "/core/overview/",
|
|
"text": "Async Overview\n\n\nComing soon.",
|
|
"title": "Overview"
|
|
},
|
|
{
|
|
"location": "/core/overview/#async-overview",
|
|
"text": "Coming soon.",
|
|
"title": "Async Overview"
|
|
},
|
|
{
|
|
"location": "/crypto/getting-started/",
|
|
"text": "Using Crypto\n\n\nCrypto (\nvapor/crypto\n) is a library containing common APIs related to cryptography and data generation. The package contains two modules:\n\n\n\n\nCrypto\n\n\nRandom\n\n\n\n\nWith Vapor\n\n\nThis package is included with Vapor by default, just add:\n\n\nimport\n \nCrypto\n\n\nimport\n \nRandom\n\n\n\n\n\n\nWithout Vapor\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\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/crypto.git\n,\n \n.\nupToNextMajor\n(\nfrom\n:\n \nx.0.0\n)),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nCrypto\n,\n \nRandom\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Crypto\n to access Crypto's APIs and \nimport Random\n to access Random's APIs.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/crypto/getting-started/#using-crypto",
|
|
"text": "Crypto ( vapor/crypto ) is a library containing common APIs related to cryptography and data generation. The package contains two modules: Crypto Random",
|
|
"title": "Using Crypto"
|
|
},
|
|
{
|
|
"location": "/crypto/getting-started/#with-vapor",
|
|
"text": "This package is included with Vapor by default, just add: import Crypto import Random",
|
|
"title": "With Vapor"
|
|
},
|
|
{
|
|
"location": "/crypto/getting-started/#without-vapor",
|
|
"text": "To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . package ( url : https://github.com/vapor/crypto.git , . upToNextMajor ( from : x.0.0 )), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ Crypto , Random , ... ]) \n ] ) Use import Crypto to access Crypto's APIs and import Random to access Random's APIs.",
|
|
"title": "Without Vapor"
|
|
},
|
|
{
|
|
"location": "/crypto/digests/",
|
|
"text": "Message Digests\n\n\nCryptographic hash functions (also known as message digest algorithms) convert data of arbitrary size to a fixed-size digest. These are most often used for generating checksums or identifiers for large data blobs.\n\n\nRead more about \nCryptographic hash functions\n on Wikipedia.\n\n\nHash\n\n\nUse the global convenience variables to create hashes using common algorithms.\n\n\nimport\n \nCrypto\n\n\n\nlet\n \ndigest\n \n=\n \ntry\n \nSHA1\n.\nhash\n(\nhello\n)\n\n\nprint\n(\ndigest\n.\nhexEncodedString\n())\n \n// aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d\n\n\n\n\n\n\nSee the Crypto module's \nglobal variables\n for a list of all available hash algorithms.\n\n\nStreaming\n\n\nYou can create a \nDigest\n manually and use its instance methods to create a hash for one or more data chunks.\n\n\nvar\n \nsha256\n \n=\n \ntry\n \nDigest\n(\nalgorithm\n:\n \n.\nsha256\n)\n\n\ntry\n \nsha256\n.\nreset\n()\n\n\ntry\n \nsha256\n.\nupdate\n(\ndata\n:\n \nhello\n)\n\n\ntry\n \nsha256\n.\nupdate\n(\ndata\n:\n \nworld\n)\n\n\nlet\n \ndigest\n \n=\n \ntry\n \nsha256\n.\nfinish\n()\n\n\nprint\n(\ndigest\n)\n \n/// Data\n\n\n\n\n\n\nBCrypt\n\n\nBCrypt is a popular hashing algorithm that has configurable complexity and handles salting automatically.\n\n\nHash\n\n\nUse the \nhash(_:cost:salt:)\n method to create BCrypt hashes.\n\n\nlet\n \ndigest\n \n=\n \ntry\n \nBCrypt\n.\nhash\n(\nvapor\n,\n \ncost\n:\n \n4\n)\n\n\nprint\n(\ndigest\n)\n \n/// data\n\n\n\n\n\n\nIncreasing the \ncost\n value will make hashing and verification take longer.\n\n\nVerify\n\n\nUse the \nverify(_:created:)\n method to verify that a BCrypt hash was created by a given plaintext input.\n\n\nlet\n \nhash\n \n=\n \ntry\n \nBCrypt\n.\nhash\n(\nvapor\n,\n \ncost\n:\n \n4\n)\n\n\ntry\n \nBCrypt\n.\nverify\n(\nvapor\n,\n \ncreated\n:\n \nhash\n)\n \n// true\n\n\ntry\n \nBCrypt\n.\nverify\n(\nfoo\n,\n \ncreated\n:\n \nhash\n)\n \n// false\n\n\n\n\n\n\nHMAC\n\n\nHMAC is an algorithm for creating \nkeyed\n hashes. HMAC will generate different hashes for the same input if different keys are used.\n\n\nlet\n \ndigest\n \n=\n \ntry\n \nHMAC\n.\nSHA1\n.\nauthenticate\n(\nvapor\n,\n \nkey\n:\n \nsecret\n)\n \n\nprint\n(\ndigest\n.\nhexEncodedString\n())\n \n// digest\n\n\n\n\n\n\nSee the \nHMAC\n class for a list of all available hash algorithms.\n\n\nStreaming\n\n\nHMAC hashes can also be streamed. The API is identical to \nhash streaming\n.",
|
|
"title": "Digests"
|
|
},
|
|
{
|
|
"location": "/crypto/digests/#message-digests",
|
|
"text": "Cryptographic hash functions (also known as message digest algorithms) convert data of arbitrary size to a fixed-size digest. These are most often used for generating checksums or identifiers for large data blobs. Read more about Cryptographic hash functions on Wikipedia.",
|
|
"title": "Message Digests"
|
|
},
|
|
{
|
|
"location": "/crypto/digests/#hash",
|
|
"text": "Use the global convenience variables to create hashes using common algorithms. import Crypto let digest = try SHA1 . hash ( hello ) print ( digest . hexEncodedString ()) // aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d See the Crypto module's global variables for a list of all available hash algorithms.",
|
|
"title": "Hash"
|
|
},
|
|
{
|
|
"location": "/crypto/digests/#streaming",
|
|
"text": "You can create a Digest manually and use its instance methods to create a hash for one or more data chunks. var sha256 = try Digest ( algorithm : . sha256 ) try sha256 . reset () try sha256 . update ( data : hello ) try sha256 . update ( data : world ) let digest = try sha256 . finish () print ( digest ) /// Data",
|
|
"title": "Streaming"
|
|
},
|
|
{
|
|
"location": "/crypto/digests/#bcrypt",
|
|
"text": "BCrypt is a popular hashing algorithm that has configurable complexity and handles salting automatically.",
|
|
"title": "BCrypt"
|
|
},
|
|
{
|
|
"location": "/crypto/digests/#hash_1",
|
|
"text": "Use the hash(_:cost:salt:) method to create BCrypt hashes. let digest = try BCrypt . hash ( vapor , cost : 4 ) print ( digest ) /// data Increasing the cost value will make hashing and verification take longer.",
|
|
"title": "Hash"
|
|
},
|
|
{
|
|
"location": "/crypto/digests/#verify",
|
|
"text": "Use the verify(_:created:) method to verify that a BCrypt hash was created by a given plaintext input. let hash = try BCrypt . hash ( vapor , cost : 4 ) try BCrypt . verify ( vapor , created : hash ) // true try BCrypt . verify ( foo , created : hash ) // false",
|
|
"title": "Verify"
|
|
},
|
|
{
|
|
"location": "/crypto/digests/#hmac",
|
|
"text": "HMAC is an algorithm for creating keyed hashes. HMAC will generate different hashes for the same input if different keys are used. let digest = try HMAC . SHA1 . authenticate ( vapor , key : secret ) print ( digest . hexEncodedString ()) // digest See the HMAC class for a list of all available hash algorithms.",
|
|
"title": "HMAC"
|
|
},
|
|
{
|
|
"location": "/crypto/digests/#streaming_1",
|
|
"text": "HMAC hashes can also be streamed. The API is identical to hash streaming .",
|
|
"title": "Streaming"
|
|
},
|
|
{
|
|
"location": "/crypto/ciphers/",
|
|
"text": "Cipher Algorithms\n\n\nCiphers allow you to encrypt plaintext data with a key yielding ciphertext. This ciphertext can be later decrypted by the same cipher using the same key.\n\n\nRead more about \nciphers\n on Wikipedia.\n\n\nEncrypt\n\n\nUse the global convenience variables for encrypting data with common algorithms.\n\n\nlet\n \nciphertext\n \n=\n \ntry\n \nAES128\n.\nencrypt\n(\nvapor\n,\n \nkey\n:\n \nsecret\n)\n\n\nprint\n(\nciphertext\n)\n \n/// Data\n\n\n\n\n\n\nDecrypt\n\n\nDecryption works very similarly to \nencryption\n. The following snippet shows how to decrypt the ciphertext from our previous example.\n\n\nlet\n \nplaintext\n \n=\n \ntry\n \nAES128\n.\ndecrypt\n(\nciphertext\n,\n \nkey\n:\n \nsecret\n)\n\n\nprint\n(\nplaintext\n)\n \n/// \nvapor\n\n\n\n\n\n\nSee the Crypto module's \nglobal variables\n for a list of all available cipher algorithms.\n\n\nStreaming\n\n\nBoth encryption and decryption can work in a streaming mode that allows data to be chunked. This is useful for controlling memory usage while encrypting large amounts of data.\n\n\nlet\n \nkey\n:\n \nData\n \n// 16-bytes\n\n\nlet\n \naes128\n \n=\n \nCipher\n(\nalgorithm\n:\n \n.\naes128ecb\n)\n\n\ntry\n \naes128\n.\nreset\n(\nkey\n:\n \nkey\n,\n \nmode\n:\n \n.\nencrypt\n)\n\n\nvar\n \nbuffer\n \n=\n \nData\n()\n\n\ntry\n \naes128\n.\nupdate\n(\ndata\n:\n \nhello\n,\n \ninto\n:\n \nbuffer\n)\n\n\ntry\n \naes128\n.\nupdate\n(\ndata\n:\n \nworld\n,\n \ninto\n:\n \nbuffer\n)\n\n\ntry\n \naes128\n.\nfinish\n(\ninto\n:\n \nbuffer\n)\n\n\nprint\n(\nbuffer\n)\n \n// Completed ciphertext",
|
|
"title": "Ciphers"
|
|
},
|
|
{
|
|
"location": "/crypto/ciphers/#cipher-algorithms",
|
|
"text": "Ciphers allow you to encrypt plaintext data with a key yielding ciphertext. This ciphertext can be later decrypted by the same cipher using the same key. Read more about ciphers on Wikipedia.",
|
|
"title": "Cipher Algorithms"
|
|
},
|
|
{
|
|
"location": "/crypto/ciphers/#encrypt",
|
|
"text": "Use the global convenience variables for encrypting data with common algorithms. let ciphertext = try AES128 . encrypt ( vapor , key : secret ) print ( ciphertext ) /// Data",
|
|
"title": "Encrypt"
|
|
},
|
|
{
|
|
"location": "/crypto/ciphers/#decrypt",
|
|
"text": "Decryption works very similarly to encryption . The following snippet shows how to decrypt the ciphertext from our previous example. let plaintext = try AES128 . decrypt ( ciphertext , key : secret ) print ( plaintext ) /// vapor See the Crypto module's global variables for a list of all available cipher algorithms.",
|
|
"title": "Decrypt"
|
|
},
|
|
{
|
|
"location": "/crypto/ciphers/#streaming",
|
|
"text": "Both encryption and decryption can work in a streaming mode that allows data to be chunked. This is useful for controlling memory usage while encrypting large amounts of data. let key : Data // 16-bytes let aes128 = Cipher ( algorithm : . aes128ecb ) try aes128 . reset ( key : key , mode : . encrypt ) var buffer = Data () try aes128 . update ( data : hello , into : buffer ) try aes128 . update ( data : world , into : buffer ) try aes128 . finish ( into : buffer ) print ( buffer ) // Completed ciphertext",
|
|
"title": "Streaming"
|
|
},
|
|
{
|
|
"location": "/crypto/asymmetric/",
|
|
"text": "Asymmetric Cryptography\n\n\nAsymmetric cryptography (also called public-key cryptography) is a cryptographic system that uses multiple keys\nusually a \"public\" and \"private\" key.\n\n\nRead more about \npublic-key cryptography\n on Wikipedia.\n\n\nRSA\n\n\nA popular asymmetric cryptography algorithm is RSA. RSA has two key types: public and private.\n\n\nRSA can create signatures from any data using a private key. \n\n\nlet\n \nprivateKey\n:\n \nString\n \n=\n \n...\n\n\nlet\n \nsignature\n \n=\n \ntry\n \nRSA\n.\nSHA512\n.\nsign\n(\nvapor\n,\n \nkey\n:\n \n.\nprivate\n(\npem\n:\n \nprivateKey\n))\n\n\n\n\n\n\n\n\nInfo\n\n\nOnly private keys can \ncreate\n signatures.\n\n\n\n\nThese signatures can be verified against the same data later using either the public or private key.\n\n\nlet\n \npublicKey\n:\n \nString\n \n=\n \n...\n\n\ntry\n \nRSA\n.\nSHA512\n.\nverify\n(\nsignature\n,\n \nsigns\n:\n \nvapor\n,\n \nkey\n:\n \n.\npublic\n(\npem\n:\n \npublicKey\n))\n \n// true\n\n\n\n\n\n\nIf RSA verifies that a signature matches input data for a public key, you can be sure that whoever generated that signature had access to that key's private key.\n\n\nAlgorithms\n\n\nRSA supports any of the Crypto module's \nDigestAlgorithm\n.\n\n\nlet\n \nprivateKey\n:\n \nString\n \n=\n \n...\n\n\nlet\n \nsignature512\n \n=\n \ntry\n \nRSA\n.\nSHA512\n.\nsign\n(\nvapor\n,\n \nkey\n:\n \n.\nprivate\n(\npem\n:\n \nprivateKey\n))\n\n\nlet\n \nsignature256\n \n=\n \ntry\n \nRSA\n.\nSHA256\n.\nsign\n(\nvapor\n,\n \nkey\n:\n \n.\nprivate\n(\npem\n:\n \nprivateKey\n))",
|
|
"title": "Asymmetric"
|
|
},
|
|
{
|
|
"location": "/crypto/asymmetric/#asymmetric-cryptography",
|
|
"text": "Asymmetric cryptography (also called public-key cryptography) is a cryptographic system that uses multiple keys usually a \"public\" and \"private\" key. Read more about public-key cryptography on Wikipedia.",
|
|
"title": "Asymmetric Cryptography"
|
|
},
|
|
{
|
|
"location": "/crypto/asymmetric/#rsa",
|
|
"text": "A popular asymmetric cryptography algorithm is RSA. RSA has two key types: public and private. RSA can create signatures from any data using a private key. let privateKey : String = ... let signature = try RSA . SHA512 . sign ( vapor , key : . private ( pem : privateKey )) Info Only private keys can create signatures. These signatures can be verified against the same data later using either the public or private key. let publicKey : String = ... try RSA . SHA512 . verify ( signature , signs : vapor , key : . public ( pem : publicKey )) // true If RSA verifies that a signature matches input data for a public key, you can be sure that whoever generated that signature had access to that key's private key.",
|
|
"title": "RSA"
|
|
},
|
|
{
|
|
"location": "/crypto/asymmetric/#algorithms",
|
|
"text": "RSA supports any of the Crypto module's DigestAlgorithm . let privateKey : String = ... let signature512 = try RSA . SHA512 . sign ( vapor , key : . private ( pem : privateKey )) let signature256 = try RSA . SHA256 . sign ( vapor , key : . private ( pem : privateKey ))",
|
|
"title": "Algorithms"
|
|
},
|
|
{
|
|
"location": "/crypto/random/",
|
|
"text": "Random\n\n\nThe \nRandom\n module deals with random data generation including random number generation.\n\n\nData Generator\n\n\nThe \nDataGenerator\n class powers all of the random data generators.\n\n\nImplementations\n\n\n\n\n\n\nOSRandom\n: Provides a random data generator using a platform-specific method.\n\n\n\n\n\n\nURandom\n provides random data generation based on the \n/dev/urandom\n file.\n\n\n\n\n\n\nCryptoRandom\n from the \nCrypto\n module provides cryptographically-secure random data using OpenSSL.\n\n\n\n\n\n\nlet\n \nrandom\n:\n \nDataGenerator\n \n...\n\n\nlet\n \ndata\n \n=\n \ntry\n \nrandom\n.\ngenerateData\n(\nbytes\n:\n \n8\n)\n\n\n\n\n\n\nGenerate\n\n\nDataGenerator\ns are capable of generating random primitive types using the \ngenerate(_:)\n method.\n\n\nlet\n \nint\n \n=\n \ntry\n \nOSRandom\n().\ngenerate\n(\nInt\n.\nself\n)\n\n\nprint\n(\nint\n)\n \n// Int",
|
|
"title": "Random"
|
|
},
|
|
{
|
|
"location": "/crypto/random/#random",
|
|
"text": "The Random module deals with random data generation including random number generation.",
|
|
"title": "Random"
|
|
},
|
|
{
|
|
"location": "/crypto/random/#data-generator",
|
|
"text": "The DataGenerator class powers all of the random data generators.",
|
|
"title": "Data Generator"
|
|
},
|
|
{
|
|
"location": "/crypto/random/#implementations",
|
|
"text": "OSRandom : Provides a random data generator using a platform-specific method. URandom provides random data generation based on the /dev/urandom file. CryptoRandom from the Crypto module provides cryptographically-secure random data using OpenSSL. let random : DataGenerator ... let data = try random . generateData ( bytes : 8 )",
|
|
"title": "Implementations"
|
|
},
|
|
{
|
|
"location": "/crypto/random/#generate",
|
|
"text": "DataGenerator s are capable of generating random primitive types using the generate(_:) method. let int = try OSRandom (). generate ( Int . self ) print ( int ) // Int",
|
|
"title": "Generate"
|
|
},
|
|
{
|
|
"location": "/database-kit/getting-started/",
|
|
"text": "Getting Started with Database Kit\n\n\nDatabase Kit (\nvapor/database-kit\n) is a framework for configuring and working with database connections. It includes core services like caching, logging, and connection pooling.\n\n\n\n\nTip\n\n\nIf you use Fluent, you will usually not need to use Database Kit manually. \nBut learning the APIs may come in handy.\n\n\n\n\nPackage\n\n\nThe Database Kit package is lightweight, pure Swift, and has few dependencies. This means it can be used as a core database framework for any Swift project\u2014even one not using Vapor.\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\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/database-kit.git\n,\n \nfrom\n:\n \n1.0.0\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nDatabaseKit\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport DatabaseKit\n to access the APIs.\n\n\nAPI Docs\n\n\nThe rest of this guide will give you an overview of what is available in the DatabaseKit package. As always, feel free to visit the \nAPI docs\n for more in-depth information.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/database-kit/getting-started/#getting-started-with-database-kit",
|
|
"text": "Database Kit ( vapor/database-kit ) is a framework for configuring and working with database connections. It includes core services like caching, logging, and connection pooling. Tip If you use Fluent, you will usually not need to use Database Kit manually. \nBut learning the APIs may come in handy.",
|
|
"title": "Getting Started with Database Kit"
|
|
},
|
|
{
|
|
"location": "/database-kit/getting-started/#package",
|
|
"text": "The Database Kit package is lightweight, pure Swift, and has few dependencies. This means it can be used as a core database framework for any Swift project\u2014even one not using Vapor. To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . package ( url : https://github.com/vapor/database-kit.git , from : 1.0.0 ), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ DatabaseKit , ... ]) \n ] ) Use import DatabaseKit to access the APIs.",
|
|
"title": "Package"
|
|
},
|
|
{
|
|
"location": "/database-kit/getting-started/#api-docs",
|
|
"text": "The rest of this guide will give you an overview of what is available in the DatabaseKit package. As always, feel free to visit the API docs for more in-depth information.",
|
|
"title": "API Docs"
|
|
},
|
|
{
|
|
"location": "/database-kit/overview/",
|
|
"text": "Using Database Kit\n\n\nDatabase Kit is a framework for configuring and working with database connections. It helps you do things like manage and pool connections, create keyed caches, and log queries. \n\n\nMany of Vapor's packages such as the Fluent drivers, Redis, and Vapor core are built on top of Database Kit. This guide will walk you through some of the common APIs you might encounter when using Database Kit.\n\n\nConfig\n\n\nYour first interaction with Database Kit will most likely be with the \nDatabasesConfig\n struct. This type helps you configure one or more databases to your application and will ultimately yield a \nDatabases\n struct. This usually takes place in \nconfigure.swift\n.\n\n\n// Create a SQLite database.\n\n\nlet\n \nsqliteDB\n \n=\n \nSQLiteDatabase\n(...)\n\n\n\n// Create a new, empty DatabasesConfig.\n\n\nvar\n \ndbsConfig\n \n=\n \nDatabasesConfig\n()\n\n\n\n// Register the SQLite database using \n.sqlite\n as an identifier.\n\n\ndbsConfig\n.\nadd\n(\nsqliteDB\n,\n \nas\n:\n \n.\nsqlite\n)\n\n\n\n// Register more DBs here if you want\n\n\n\n// Register the DatabaseConfig to services.\n\n\nservices\n.\nregister\n(\ndbsConfig\n)\n\n\n\n\n\n\nUsing the \nadd(...)\n methods, you can register \nDatabase\ns to the config. You can register instances of a database, a database type, or a closure that creates a database. The latter two methods will be resolved when your container boots.\n\n\nYou can also configure options on your databases, such as enabling logging.\n\n\n// Enable logging on the SQLite database\n\n\ndbsConfig\n.\nenableLogging\n(\nfor\n:\n \n.\nsqlite\n)\n\n\n\n\n\n\nSee the section on \nlogging\n for more information.\n\n\nIdentifier\n\n\nMost database integrations will provide a default \nDatabaseIdentifier\n to use. However, you can always create your own. This is usually done by creating a static extension.\n\n\nextension\n \nDatabaseIdentifier\n \n{\n\n \n/// Test database.\n\n \nstatic\n \nvar\n \ntesting\n:\n \nDatabaseIdentifier\nMySQLDatabase\n \n{\n\n \nreturn\n \ntesting\n\n \n}\n\n\n}\n\n\n\n\n\n\nDatabaseIdentifier\n is \nExpressibleByStringLiteral\n which allows you to create one with just a \nString\n.\n\n\nDatabases\n\n\nOnce you have registered a \nDatabasesConfig\n to your services and booted a container, you can take advantage of the convenience extensions on \nContainer\n to start creating connections.\n\n\n// Creates a new connection to `.sqlite` db\n\n\napp\n.\nwithNewConnection\n(\nto\n:\n \n.\nsqlite\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \nconn\n.\nquery\n(...)\n \n// do some db query\n\n\n}\n\n\n\n\n\n\nRead more about creating and managing connections in the next section.\n\n\nConnections\n\n\nDatabase Kit's main focus is on creating, managing, and pooling connections. Creating new connections takes a non-trivial amount of time for your application and many cloud services limit the total number of connections to a service that can be open. Because of this, it is important for high-concurrency web applications to manage their connections carefully.\n\n\nPools\n\n\nA common solution to connection management is the use of connection pools. These pools usually have a set maximum number of connections that are allowed to be open at once. Each time the pool is asked for a connection, it will first check if one is available before creating a new connection. If none are available, it will create a new one. If no connections are available and the pool is already at its maximum, the request for a new connection will \nwait\n for a connection to be returned. \n\n\nThe easiest way to request and release a pooled connection is the method \nwithPooledConnection(...)\n. \n\n\n// Requests a pooled connection to `.psql` db\n\n\nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\npsql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \nconn\n.\nquery\n(...)\n \n// do some db query\n\n\n}\n\n\n\n\n\n\nThis method will requested a pooled connection to the identified database and call the provided closure when the connection is available. When the \nFuture\n returned by the closure has completed, the connection will automatically be returned to the pool.\n\n\nIf you need access to a connection outside of a closure, you can use the related request / release methods instead.\n\n\n// Request a connection from the pool and wait for it to be ready.\n\n\nlet\n \nconn\n \n=\n \ntry\n \napp\n.\nrequestPooledConnection\n(\nto\n:\n \n.\npsql\n).\nwait\n()\n\n\n\n// Ensure the connection is released when we exit this scope.\n\n\ndefer\n \n{\n \napp\n.\nreleasePooledConnection\n(\nconn\n,\n \nto\n:\n \n.\npsql\n)\n \n}\n\n\n\n\n\n\nYou can configure your connection pools using the \nDatabaseConnectionPoolConfig\n struct. \n\n\n// Create a new, empty pool config.\n\n\nvar\n \npoolConfig\n \n=\n \nDatabaseConnectionPoolConfig\n()\n\n\n\n// Set max connections per pool to 8.\n\n\npoolConfig\n.\nmaxConnections\n \n=\n \n8\n\n\n\n// Register the pool config.\n\n\nservices\n.\nregister\n(\npoolConfig\n)\n\n\n\n\n\n\nTo prevent race conditions, pools are never shared between event loops. There is usually one pool per database per event loop. This means that the amount of connections your application can potentially open to a given database is equal to \nnumThreads * maxConns\n.\n\n\nNew\n\n\nYou can always create a new connection to your databases if you need to. This will not affect your pooled connections. Creating new connections is especially useful during testing and app boot. But try not to do it in route closures since heavy traffic to your app could end up creating a lot of connections!\n\n\nSimilar to pooled connections, opening and closing new connections can be done using \nwithNewConnection(...)\n. \n\n\n// Creates a new connection to `.sqlite` db\n\n\napp\n.\nwithNewConnection\n(\nto\n:\n \n.\nsqlite\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \nconn\n.\nquery\n(...)\n \n// do some db query\n\n\n}\n\n\n\n\n\n\nThis method will create a new connection, calling the supplied closure when the connection is open. When the \nFuture\n returned in the closure completes, the connection will be closed automatically.\n\n\nYou can also simply open a new connection with \nnewConnection(...)\n.\n\n\n// Creates a new connection to `.sqlite` db\n\n\nlet\n \nconn\n \n=\n \ntry\n \napp\n.\nnewConnection\n(\nto\n:\n \n.\nsqlite\n).\nwait\n()\n\n\n\n// Ensure the connection is closed when we exit this scope.\n\n\ndefer\n \n{\n \nconn\n.\nclose\n()\n \n}\n\n\n\n\n\n\nLogging\n\n\nDatabases can opt into supporting query logging via the \nLogSupporting\n protocol. Databases that conform to this protocol can have loggers \nconfigured\n via \nDatabasesConfig\n.\n\n\n// Enable logging on the SQLite database\n\n\ndbsConfig\n.\nenableLogging\n(\nfor\n:\n \n.\nsqlite\n)\n\n\n\n\n\n\nBy default, a simple print logger will be used, but you can pass a custom \nDatabaseLogHandler\n.\n\n\n// Create a custom log handler.\n\n\nlet\n \nmyLogger\n:\n \nDatabaseLogHandler\n \n=\n \n...\n\n\n\n// Enable logging on SQLite w/ custom logger.\n\n\ndbsConfig\n.\nenableLogging\n(\nfor\n:\n \n.\nsqlite\n,\n \nlogger\n:\n \nmyLogger\n)\n\n\n\n\n\n\nLog handlers will receive an instance of \nDatabaseLog\n for each logged query. This contains information such as the query, parameterized values, database id, and time.\n\n\nKeyed Cache\n\n\nDatabases can opt into supporting keyed-caching via the \nKeyedCacheSupporting\n protocol. Databases that conform to this protocol can be used to create instances of \nDatabaseKeyedCache\n.\n\n\nKeyed caches are capable of getting, setting, and removing \nCodable\n values at keys. They are sometimes called \"key value stores\".\n\n\nTo create a keyed cache, you can use the extensions on \nContainer\n.\n\n\n// Creates a DatabaseKeyedCache with .redis connection pool\n\n\nlet\n \ncache\n \n=\n \ntry\n \napp\n.\nkeyedCache\n(\nfor\n:\n \n.\nredis\n)\n\n\n\n// Sets \nhello\n = \nworld\n\n\ntry\n \ncache\n.\nset\n(\nhello\n,\n \nto\n:\n \nworld\n).\nwait\n()\n\n\n\n// Gets \nhello\n\n\nlet\n \nworld\n \n=\n \ntry\n \ncache\n.\nget\n(\nhello\n,\n \nas\n:\n \nString\n.\nself\n).\nwait\n()\n\n\nprint\n(\nworld\n)\n \n// \nworld\n\n\n\n// Removes \nhello\n\n\ntry\n \ncache\n.\nremove\n(\nhello\n).\nwait\n()\n\n\n\n\n\n\nSee the \nKeyedCache\n protocol for more information.\n\n\nAPI Docs\n\n\nCheck out the \nAPI docs\n for more in-depth information about DatabaseKit's APIs.",
|
|
"title": "Overview"
|
|
},
|
|
{
|
|
"location": "/database-kit/overview/#using-database-kit",
|
|
"text": "Database Kit is a framework for configuring and working with database connections. It helps you do things like manage and pool connections, create keyed caches, and log queries. Many of Vapor's packages such as the Fluent drivers, Redis, and Vapor core are built on top of Database Kit. This guide will walk you through some of the common APIs you might encounter when using Database Kit.",
|
|
"title": "Using Database Kit"
|
|
},
|
|
{
|
|
"location": "/database-kit/overview/#config",
|
|
"text": "Your first interaction with Database Kit will most likely be with the DatabasesConfig struct. This type helps you configure one or more databases to your application and will ultimately yield a Databases struct. This usually takes place in configure.swift . // Create a SQLite database. let sqliteDB = SQLiteDatabase (...) // Create a new, empty DatabasesConfig. var dbsConfig = DatabasesConfig () // Register the SQLite database using .sqlite as an identifier. dbsConfig . add ( sqliteDB , as : . sqlite ) // Register more DBs here if you want // Register the DatabaseConfig to services. services . register ( dbsConfig ) Using the add(...) methods, you can register Database s to the config. You can register instances of a database, a database type, or a closure that creates a database. The latter two methods will be resolved when your container boots. You can also configure options on your databases, such as enabling logging. // Enable logging on the SQLite database dbsConfig . enableLogging ( for : . sqlite ) See the section on logging for more information.",
|
|
"title": "Config"
|
|
},
|
|
{
|
|
"location": "/database-kit/overview/#identifier",
|
|
"text": "Most database integrations will provide a default DatabaseIdentifier to use. However, you can always create your own. This is usually done by creating a static extension. extension DatabaseIdentifier { \n /// Test database. \n static var testing : DatabaseIdentifier MySQLDatabase { \n return testing \n } } DatabaseIdentifier is ExpressibleByStringLiteral which allows you to create one with just a String .",
|
|
"title": "Identifier"
|
|
},
|
|
{
|
|
"location": "/database-kit/overview/#databases",
|
|
"text": "Once you have registered a DatabasesConfig to your services and booted a container, you can take advantage of the convenience extensions on Container to start creating connections. // Creates a new connection to `.sqlite` db app . withNewConnection ( to : . sqlite ) { conn in \n return conn . query (...) // do some db query } Read more about creating and managing connections in the next section.",
|
|
"title": "Databases"
|
|
},
|
|
{
|
|
"location": "/database-kit/overview/#connections",
|
|
"text": "Database Kit's main focus is on creating, managing, and pooling connections. Creating new connections takes a non-trivial amount of time for your application and many cloud services limit the total number of connections to a service that can be open. Because of this, it is important for high-concurrency web applications to manage their connections carefully.",
|
|
"title": "Connections"
|
|
},
|
|
{
|
|
"location": "/database-kit/overview/#pools",
|
|
"text": "A common solution to connection management is the use of connection pools. These pools usually have a set maximum number of connections that are allowed to be open at once. Each time the pool is asked for a connection, it will first check if one is available before creating a new connection. If none are available, it will create a new one. If no connections are available and the pool is already at its maximum, the request for a new connection will wait for a connection to be returned. The easiest way to request and release a pooled connection is the method withPooledConnection(...) . // Requests a pooled connection to `.psql` db req . withPooledConnection ( to : . psql ) { conn in \n return conn . query (...) // do some db query } This method will requested a pooled connection to the identified database and call the provided closure when the connection is available. When the Future returned by the closure has completed, the connection will automatically be returned to the pool. If you need access to a connection outside of a closure, you can use the related request / release methods instead. // Request a connection from the pool and wait for it to be ready. let conn = try app . requestPooledConnection ( to : . psql ). wait () // Ensure the connection is released when we exit this scope. defer { app . releasePooledConnection ( conn , to : . psql ) } You can configure your connection pools using the DatabaseConnectionPoolConfig struct. // Create a new, empty pool config. var poolConfig = DatabaseConnectionPoolConfig () // Set max connections per pool to 8. poolConfig . maxConnections = 8 // Register the pool config. services . register ( poolConfig ) To prevent race conditions, pools are never shared between event loops. There is usually one pool per database per event loop. This means that the amount of connections your application can potentially open to a given database is equal to numThreads * maxConns .",
|
|
"title": "Pools"
|
|
},
|
|
{
|
|
"location": "/database-kit/overview/#new",
|
|
"text": "You can always create a new connection to your databases if you need to. This will not affect your pooled connections. Creating new connections is especially useful during testing and app boot. But try not to do it in route closures since heavy traffic to your app could end up creating a lot of connections! Similar to pooled connections, opening and closing new connections can be done using withNewConnection(...) . // Creates a new connection to `.sqlite` db app . withNewConnection ( to : . sqlite ) { conn in \n return conn . query (...) // do some db query } This method will create a new connection, calling the supplied closure when the connection is open. When the Future returned in the closure completes, the connection will be closed automatically. You can also simply open a new connection with newConnection(...) . // Creates a new connection to `.sqlite` db let conn = try app . newConnection ( to : . sqlite ). wait () // Ensure the connection is closed when we exit this scope. defer { conn . close () }",
|
|
"title": "New"
|
|
},
|
|
{
|
|
"location": "/database-kit/overview/#logging",
|
|
"text": "Databases can opt into supporting query logging via the LogSupporting protocol. Databases that conform to this protocol can have loggers configured via DatabasesConfig . // Enable logging on the SQLite database dbsConfig . enableLogging ( for : . sqlite ) By default, a simple print logger will be used, but you can pass a custom DatabaseLogHandler . // Create a custom log handler. let myLogger : DatabaseLogHandler = ... // Enable logging on SQLite w/ custom logger. dbsConfig . enableLogging ( for : . sqlite , logger : myLogger ) Log handlers will receive an instance of DatabaseLog for each logged query. This contains information such as the query, parameterized values, database id, and time.",
|
|
"title": "Logging"
|
|
},
|
|
{
|
|
"location": "/database-kit/overview/#keyed-cache",
|
|
"text": "Databases can opt into supporting keyed-caching via the KeyedCacheSupporting protocol. Databases that conform to this protocol can be used to create instances of DatabaseKeyedCache . Keyed caches are capable of getting, setting, and removing Codable values at keys. They are sometimes called \"key value stores\". To create a keyed cache, you can use the extensions on Container . // Creates a DatabaseKeyedCache with .redis connection pool let cache = try app . keyedCache ( for : . redis ) // Sets hello = world try cache . set ( hello , to : world ). wait () // Gets hello let world = try cache . get ( hello , as : String . self ). wait () print ( world ) // world // Removes hello try cache . remove ( hello ). wait () See the KeyedCache protocol for more information.",
|
|
"title": "Keyed Cache"
|
|
},
|
|
{
|
|
"location": "/database-kit/overview/#api-docs",
|
|
"text": "Check out the API docs for more in-depth information about DatabaseKit's APIs.",
|
|
"title": "API Docs"
|
|
},
|
|
{
|
|
"location": "/fluent/getting-started/",
|
|
"text": "Warning\n\n\nFluent 3.0 is still in beta. Some documentation may be missing or out of date.\n\n\n\n\nGetting Started with Fluent\n\n\nFluent (\nvapor/fluent\n) is a type-safe, fast, and easy-to-use ORM framework built for Swift.\nIt takes advantage of Swift's strong type system to provide an elegant foundation for building database integrations.\n\n\nDatabase\n\n\nFluent is just a framework for building ORMs, not an ORM itself. To get started using Fluent, pick one of the databases below.\n\n\n\n\n\n\n\n\ndatabase\n\n\nlibrary\n\n\ndriver\n\n\nguide\n\n\n\n\n\n\n\n\n\n\nPostgreSQL\n\n\nvapor/postgresql\n\n\nvapor/fluent-postgresql\n\n\nGetting Started \n\n\n\n\n\n\nMySQL\n\n\nvapor/mysql\n\n\nvapor/fluent-mysql\n\n\nGetting Started \n\n\n\n\n\n\nSQLite\n\n\nvapor/sqlite\n\n\nvapor/fluent-sqlite\n\n\nGetting Started \n\n\n\n\n\n\n\n\nAfter you get started, come back to the other sections in this guide for a more in-depth look at Fluent's features.\n\n\n\n\nTip\n\n\nYou can also search GitHub for the tag \nfluent-database\n for a full list of official and third-party Fluent databases.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/fluent/getting-started/#getting-started-with-fluent",
|
|
"text": "Fluent ( vapor/fluent ) is a type-safe, fast, and easy-to-use ORM framework built for Swift.\nIt takes advantage of Swift's strong type system to provide an elegant foundation for building database integrations.",
|
|
"title": "Getting Started with Fluent"
|
|
},
|
|
{
|
|
"location": "/fluent/getting-started/#database",
|
|
"text": "Fluent is just a framework for building ORMs, not an ORM itself. To get started using Fluent, pick one of the databases below. database library driver guide PostgreSQL vapor/postgresql vapor/fluent-postgresql Getting Started MySQL vapor/mysql vapor/fluent-mysql Getting Started SQLite vapor/sqlite vapor/fluent-sqlite Getting Started After you get started, come back to the other sections in this guide for a more in-depth look at Fluent's features. Tip You can also search GitHub for the tag fluent-database for a full list of official and third-party Fluent databases.",
|
|
"title": "Database"
|
|
},
|
|
{
|
|
"location": "/fluent/models/",
|
|
"text": "Fluent Models\n\n\nModels are the heart of Fluent. Unlike ORMs in other languages, Fluent doesn't return untyped arrays or dictionaries for queries. Instead, you query the database using models. This allows the Swift compiler to catch many errors that have burdened ORM users for ages.\n\n\n\n\nInfo\n\n\nThis guide provides an overview of the \nModel\n protocol and its associated methods and properties. If you are just getting started, check out the guide for your database at \nFluent \n Getting Started\n.\n\n\n\n\nModel\n is a protocol in the \nFluent\n module. It extends the \nAnyModel\n protocol which can be used for type-erasure. \n\n\nConformance\n\n\nBoth \nstruct\ns and \nclass\nes can conform to \nModel\n, however you must pay special attention to Fluent's return types if you use a \nstruct\n. Since Fluent works asynchronously, any mutations to a value-type (\nstruct\n) model must return a new copy of the model as a future result.\n\n\nNormally, you will conform your model to one of the convenience models available in your database-specific package (i.e., \nPostgreSQLModel\n). However, if you want to customize additional properties, such as the model's \nidKey\n, you will want to use the \nModel\n protocol itself.\n\n\nLet's take a look at what a basic \nModel\n conformance looks like.\n\n\n/// A simple user.\n\n\nfinal\n \nclass\n \nUser\n:\n \nModel\n \n{\n\n \n/// See `Model.Database`\n\n \ntypealias\n \nDatabase\n \n=\n \nFooDatabase\n\n\n \n/// See `Model.ID`\n\n \ntypealias\n \nID\n \n=\n \nInt\n\n\n \n/// See `Model.idKey`\n\n \nstatic\n \nlet\n \nidKey\n:\n \nIDKey\n \n=\n \n\\\n.\nid\n\n\n \n/// The unique identifier for this user.\n\n \nvar\n \nid\n:\n \nInt\n?\n\n\n \n/// The user\ns full name.\n\n \nvar\n \nname\n:\n \nString\n\n\n \n/// The user\ns current age in years.\n\n \nvar\n \nage\n:\n \nInt\n\n\n \n/// Creates a new user.\n\n \ninit\n(\nid\n:\n \nInt\n?\n \n=\n \nnil\n,\n \nname\n:\n \nString\n,\n \nage\n:\n \nInt\n)\n \n{\n\n \nself\n.\nid\n \n=\n \nid\n\n \nself\n.\nname\n \n=\n \nname\n\n \nself\n.\nage\n \n=\n \nage\n\n \n}\n\n\n}\n\n\n\n\n\n\n\n\nTip\n\n\nUsing \nfinal\n prevents your class from being sub-classed. This makes your life easier.\n\n\n\n\nAssociated Types\n\n\nModel\n defines a few associated types that help Fluent create type-safe APIs for you to use. Take a look at \nAnyModel\n if you need a type-erased version with no associated types.\n\n\nDatabase\n\n\nThis type indicates to Fluent which database you intend to use with this model. Using this information, Fluent can dynamically add appropriate methods and data types to any \nQueryBuilder\ns you create with this model.\n\n\nfinal\n \nclass\n \nUser\n:\n \nModel\n \n{\n\n \n/// See `Model.Database`\n\n \ntypealias\n \nDatabase\n \n=\n \nFooDatabase\n\n \n/// ...\n\n\n}\n\n\n\n\n\n\nIt is possible to make this associated type generic by adding a generic type to your class or struct (i.e, \nUser\nT\n). This is useful for cases where you are attempting to create generic extensions to Fluent, like perhaps an additive service provider.\n\n\nfinal\n \nclass\n \nUser\nD\n:\n \nModel\n \nwhere\n \nD\n:\n \nDatabase\n \n{\n\n \n/// See `Model.Database`\n\n \ntypealias\n \nDatabase\n \n=\n \nD\n\n \n/// ...\n\n\n}\n\n\n\n\n\n\nYou can add further conditions to \nD\n, such as \nQuerySupporting\n or \nSchemaSupporting\n. You can also dynamically extend and conform your generic model using \nextension User where D: ... { }\n.\n\n\nThat said, for most cases, you should stick to using a concrete type-alias wherever possible. Fluent 3 is designed to allow you to harness the power of your database by creating a strong connection between your models and the underlying driver. \n\n\nID\n\n\nThis property defines the type your model will use for its unique identifier.\n\n\nfinal\n \nclass\n \nUser\n:\n \nModel\n \n{\n\n \n/// See `Model.ID`\n\n \ntypealias\n \nID\n \n=\n \nUUID\n\n \n/// ...\n\n\n}\n\n\n\n\n\n\nThis will usually be something like \nInt\n, \nUUID\n, or \nString\n although you can theoretically use any type you like.\n\n\nProperties\n\n\nThere are several overridable properties on \nModel\n that you can use to customize how Fluent interacts with your database.\n\n\nName\n\n\nThis \nString\n will be used as a unique identifier for your model whenever Fluent needs one.\n\n\nfinal\n \nclass\n \nUser\n:\n \nModel\n \n{\n\n \n/// See `Model.name`\n\n \nstatic\n \nlet\n \nname\n \n=\n \nuser\n\n \n/// ...\n\n\n}\n\n\n\n\n\n\nBy default, this is the type name of your model lowercased.\n\n\nEntity\n\n\nEntity is a generic word used to mean either \"table\" or \"collection\", depending on which type of backend you are using for Fluent.\n\n\nfinal\n \nclass\n \nGoose\n:\n \nModel\n \n{\n\n \n/// See `Model.entity`\n\n \nstatic\n \nlet\n \nentity\n \n=\n \ngeese\n\n \n/// ...\n\n\n}\n\n\n\n\n\n\nBy default, this property will be \nname\n pluralized. Overriding this property is useful in situations where language fails you and the plural form of a word is very irregular.\n\n\nID Key\n\n\nThe ID key is a writeable \nkey path\n that points to your model's unique identifier property.\n\n\nUsually this will be a property named \nid\n (for some databases it is \n_id\n). However you can theoretically use any key you like.\n\n\nfinal\n \nclass\n \nUser\n:\n \nModel\n \n{\n\n \n/// See `Model.ID`\n\n \ntypealias\n \nID\n \n=\n \nString\n\n\n \n/// See `Model.entity`\n\n \nstatic\n \nlet\n \nidKey\n \n=\n \n\\\n.\nusername\n\n\n \n/// The user\ns unique username\n\n \nvar\n \nusername\n:\n \nString\n?\n\n\n \n/// ...\n\n\n}\n\n\n\n\n\n\nThe \nidKey\n property must point to an optional, writeable (\nvar\n) property with type matching \nID\n.\n\n\nLifecycle\n\n\nThere are several lifecycle methods on \nModel\n that you can override to hook into Fluent events.\n\n\n\n\n\n\n\n\nmethod\n\n\ndescription\n\n\nthrowing\n\n\n\n\n\n\n\n\n\n\nwillCreate\n\n\nCalled before Fluent saves your model (for the first time)\n\n\nCancels the save.\n\n\n\n\n\n\ndidCreate\n\n\nCalled after Fluent saves your model (for the first time)\n\n\nSave completes. Query fails.\n\n\n\n\n\n\nwillUpdate\n\n\nCalled before Fluent saves your model (subsequent saves)\n\n\nCancels the save.\n\n\n\n\n\n\ndidUpdate\n\n\nCalled after Fluent saves your model (subsequent saves)\n\n\nSave completes. Query fails.\n\n\n\n\n\n\nwillRead\n\n\nCalled before Fluent returns your model from a fetch query.\n\n\nCancels the fetch.\n\n\n\n\n\n\nwillDelete\n\n\nCalled before Fluent deletes your model.\n\n\nCancels the delete.\n\n\n\n\n\n\n\n\nHere's an example of overriding the \nwillUpdate(on:)\n method.\n\n\nfinal\n \nclass\n \nUser\n:\n \nModel\n \n{\n\n \n/// ...\n\n\n \n/// See `Model.willUpdate(on:)`\n\n \nfunc\n \nwillUpdate\n(\non\n \nconnection\n:\n \nDatabase\n.\nConnection\n)\n \nthrows\n \n-\n \nFuture\nSelf\n \n{\n\n \n/// Throws an error if the username is invalid\n\n \ntry\n \nvalidateUsername\n()\n\n\n \n/// Return the user. No async work is being done, so we must create a future manually.\n\n \nreturn\n \nFuture\n.\nmap\n(\non\n:\n \nconnection\n)\n \n{\n \nself\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nCRUD\n\n\nThe model offers basic CRUD method (create, read, update, delete).\n\n\nCreate\n\n\nThis method creates a new row / item for an instance of your model in the database.\n\n\nIf your model does not have an ID, calls to \n.save(on:)\n will redirect to this method.\n\n\nlet\n \ndidCreate\n \n=\n \nuser\n.\ncreate\n(\non\n:\n \nreq\n)\n\n\nprint\n(\ndidCreate\n)\n \n/// Future\nUser\n\n\n\n\n\n\n\n\nInfo\n\n\nIf you are using a value-type (\nstruct\n), the instance of your model returned by \n.create(on:)\n will contain the model's new ID.\n\n\n\n\nRead\n\n\nTwo methods are important for reading your model from the database, \nfind(_:on:)\n and \nquery(on:)\n.\n\n\n/// Finds a user with ID == 1\n\n\nlet\n \nuser\n \n=\n \nUser\n.\nfind\n(\n1\n,\n \non\n:\n \nreq\n)\n\n\nprint\n(\nuser\n)\n \n/// Future\nUser?\n\n\n\n\n\n\n/// Finds all users with name == \nVapor\n\n\nlet\n \nusers\n \n=\n \nUser\n.\nquery\n(\non\n:\n \nreq\n).\nfilter\n(\n\\\n.\nname\n \n==\n \nVapor\n).\nall\n()\n\n\nprint\n(\nusers\n)\n \n/// Future\n[User]\n\n\n\n\n\n\nUpdate\n\n\nThis method updates the existing row / item associated with an instance of your model in the database.\n\n\nIf your model already has an ID, calls to \n.save(on:)\n will redirect to this method.\n\n\n/// Updates the user\n\n\nlet\n \ndidUpdate\n \n=\n \nuser\n.\nupdate\n(\non\n:\n \nreq\n)\n\n\nprint\n(\ndidUpdate\n)\n \n/// Future\nUser\n\n\n\n\n\n\nDelete\n\n\nThis method deletes the existing row / item associated with an instance of your model from the database.\n\n\n/// Deletes the user\n\n\nlet\n \ndidDelete\n \n=\n \nuser\n.\ndelete\n(\non\n:\n \nreq\n)\n\n\nprint\n(\ndidDelete\n)\n \n/// Future\nVoid\n\n\n\n\n\n\nMethods\n\n\nModel\n offers some convenience methods to make working with it easier.\n\n\nRequire ID\n\n\nThis method return's the models ID or throws an error.\n\n\nlet\n \nid\n \n=\n \ntry\n \nuser\n.\nrequireID\n()",
|
|
"title": "Models"
|
|
},
|
|
{
|
|
"location": "/fluent/models/#fluent-models",
|
|
"text": "Models are the heart of Fluent. Unlike ORMs in other languages, Fluent doesn't return untyped arrays or dictionaries for queries. Instead, you query the database using models. This allows the Swift compiler to catch many errors that have burdened ORM users for ages. Info This guide provides an overview of the Model protocol and its associated methods and properties. If you are just getting started, check out the guide for your database at Fluent Getting Started . Model is a protocol in the Fluent module. It extends the AnyModel protocol which can be used for type-erasure.",
|
|
"title": "Fluent Models"
|
|
},
|
|
{
|
|
"location": "/fluent/models/#conformance",
|
|
"text": "Both struct s and class es can conform to Model , however you must pay special attention to Fluent's return types if you use a struct . Since Fluent works asynchronously, any mutations to a value-type ( struct ) model must return a new copy of the model as a future result. Normally, you will conform your model to one of the convenience models available in your database-specific package (i.e., PostgreSQLModel ). However, if you want to customize additional properties, such as the model's idKey , you will want to use the Model protocol itself. Let's take a look at what a basic Model conformance looks like. /// A simple user. final class User : Model { \n /// See `Model.Database` \n typealias Database = FooDatabase \n\n /// See `Model.ID` \n typealias ID = Int \n\n /// See `Model.idKey` \n static let idKey : IDKey = \\ . id \n\n /// The unique identifier for this user. \n var id : Int ? \n\n /// The user s full name. \n var name : String \n\n /// The user s current age in years. \n var age : Int \n\n /// Creates a new user. \n init ( id : Int ? = nil , name : String , age : Int ) { \n self . id = id \n self . name = name \n self . age = age \n } } Tip Using final prevents your class from being sub-classed. This makes your life easier.",
|
|
"title": "Conformance"
|
|
},
|
|
{
|
|
"location": "/fluent/models/#associated-types",
|
|
"text": "Model defines a few associated types that help Fluent create type-safe APIs for you to use. Take a look at AnyModel if you need a type-erased version with no associated types.",
|
|
"title": "Associated Types"
|
|
},
|
|
{
|
|
"location": "/fluent/models/#database",
|
|
"text": "This type indicates to Fluent which database you intend to use with this model. Using this information, Fluent can dynamically add appropriate methods and data types to any QueryBuilder s you create with this model. final class User : Model { \n /// See `Model.Database` \n typealias Database = FooDatabase \n /// ... } It is possible to make this associated type generic by adding a generic type to your class or struct (i.e, User T ). This is useful for cases where you are attempting to create generic extensions to Fluent, like perhaps an additive service provider. final class User D : Model where D : Database { \n /// See `Model.Database` \n typealias Database = D \n /// ... } You can add further conditions to D , such as QuerySupporting or SchemaSupporting . You can also dynamically extend and conform your generic model using extension User where D: ... { } . That said, for most cases, you should stick to using a concrete type-alias wherever possible. Fluent 3 is designed to allow you to harness the power of your database by creating a strong connection between your models and the underlying driver.",
|
|
"title": "Database"
|
|
},
|
|
{
|
|
"location": "/fluent/models/#id",
|
|
"text": "This property defines the type your model will use for its unique identifier. final class User : Model { \n /// See `Model.ID` \n typealias ID = UUID \n /// ... } This will usually be something like Int , UUID , or String although you can theoretically use any type you like.",
|
|
"title": "ID"
|
|
},
|
|
{
|
|
"location": "/fluent/models/#properties",
|
|
"text": "There are several overridable properties on Model that you can use to customize how Fluent interacts with your database.",
|
|
"title": "Properties"
|
|
},
|
|
{
|
|
"location": "/fluent/models/#name",
|
|
"text": "This String will be used as a unique identifier for your model whenever Fluent needs one. final class User : Model { \n /// See `Model.name` \n static let name = user \n /// ... } By default, this is the type name of your model lowercased.",
|
|
"title": "Name"
|
|
},
|
|
{
|
|
"location": "/fluent/models/#entity",
|
|
"text": "Entity is a generic word used to mean either \"table\" or \"collection\", depending on which type of backend you are using for Fluent. final class Goose : Model { \n /// See `Model.entity` \n static let entity = geese \n /// ... } By default, this property will be name pluralized. Overriding this property is useful in situations where language fails you and the plural form of a word is very irregular.",
|
|
"title": "Entity"
|
|
},
|
|
{
|
|
"location": "/fluent/models/#id-key",
|
|
"text": "The ID key is a writeable key path that points to your model's unique identifier property. Usually this will be a property named id (for some databases it is _id ). However you can theoretically use any key you like. final class User : Model { \n /// See `Model.ID` \n typealias ID = String \n\n /// See `Model.entity` \n static let idKey = \\ . username \n\n /// The user s unique username \n var username : String ? \n\n /// ... } The idKey property must point to an optional, writeable ( var ) property with type matching ID .",
|
|
"title": "ID Key"
|
|
},
|
|
{
|
|
"location": "/fluent/models/#lifecycle",
|
|
"text": "There are several lifecycle methods on Model that you can override to hook into Fluent events. method description throwing willCreate Called before Fluent saves your model (for the first time) Cancels the save. didCreate Called after Fluent saves your model (for the first time) Save completes. Query fails. willUpdate Called before Fluent saves your model (subsequent saves) Cancels the save. didUpdate Called after Fluent saves your model (subsequent saves) Save completes. Query fails. willRead Called before Fluent returns your model from a fetch query. Cancels the fetch. willDelete Called before Fluent deletes your model. Cancels the delete. Here's an example of overriding the willUpdate(on:) method. final class User : Model { \n /// ... \n\n /// See `Model.willUpdate(on:)` \n func willUpdate ( on connection : Database . Connection ) throws - Future Self { \n /// Throws an error if the username is invalid \n try validateUsername () \n\n /// Return the user. No async work is being done, so we must create a future manually. \n return Future . map ( on : connection ) { self } \n } }",
|
|
"title": "Lifecycle"
|
|
},
|
|
{
|
|
"location": "/fluent/models/#crud",
|
|
"text": "The model offers basic CRUD method (create, read, update, delete).",
|
|
"title": "CRUD"
|
|
},
|
|
{
|
|
"location": "/fluent/models/#create",
|
|
"text": "This method creates a new row / item for an instance of your model in the database. If your model does not have an ID, calls to .save(on:) will redirect to this method. let didCreate = user . create ( on : req ) print ( didCreate ) /// Future User Info If you are using a value-type ( struct ), the instance of your model returned by .create(on:) will contain the model's new ID.",
|
|
"title": "Create"
|
|
},
|
|
{
|
|
"location": "/fluent/models/#read",
|
|
"text": "Two methods are important for reading your model from the database, find(_:on:) and query(on:) . /// Finds a user with ID == 1 let user = User . find ( 1 , on : req ) print ( user ) /// Future User? /// Finds all users with name == Vapor let users = User . query ( on : req ). filter ( \\ . name == Vapor ). all () print ( users ) /// Future [User]",
|
|
"title": "Read"
|
|
},
|
|
{
|
|
"location": "/fluent/models/#update",
|
|
"text": "This method updates the existing row / item associated with an instance of your model in the database. If your model already has an ID, calls to .save(on:) will redirect to this method. /// Updates the user let didUpdate = user . update ( on : req ) print ( didUpdate ) /// Future User",
|
|
"title": "Update"
|
|
},
|
|
{
|
|
"location": "/fluent/models/#delete",
|
|
"text": "This method deletes the existing row / item associated with an instance of your model from the database. /// Deletes the user let didDelete = user . delete ( on : req ) print ( didDelete ) /// Future Void",
|
|
"title": "Delete"
|
|
},
|
|
{
|
|
"location": "/fluent/models/#methods",
|
|
"text": "Model offers some convenience methods to make working with it easier.",
|
|
"title": "Methods"
|
|
},
|
|
{
|
|
"location": "/fluent/models/#require-id",
|
|
"text": "This method return's the models ID or throws an error. let id = try user . requireID ()",
|
|
"title": "Require ID"
|
|
},
|
|
{
|
|
"location": "/fluent/migrations/",
|
|
"text": "Getting Started with Migrations\n\n\nMigrations are a way of making organized, testable, and reliable changes to your database's structure--\neven while it's in production!\n\n\nMigrations are often used for preparing a database schema for your models. However, they can also be used to \nmake normal queries to your database.\n\n\nIn this guide we will cover creating both types of migrations.\n\n\nModel Schema\n\n\nLet's take a look at how we can prepare a schema supporting database to accept the \n\nUser\n model from the \nprevious section\n.\n\n\nJust like we did with the \nModel\n protocol, we will conform our \nUser\n to \nMigration\n.\n\n\nimport\n \nFluent\n\n\n\nextension\n \nUser\n:\n \nMigration\n \n{\n\n\n\n}\n\n\n\n\n\n\nSwift will inform us that \nUser\n does not yet conform. Let's add the required methods!\n\n\nPrepare\n\n\nThe first method to implement is \nprepare\n. This method is where you make any of your \ndesired changes to the database.\n\n\nFor our \nUser\n model, we simply want to create a table that can store one or more users. To do this,\nwe will use the \n.create(...)\n function on the supplied database connection.\n\n\nextension\n \nUser\n:\n \nMigration\n \n{\n\n \n/// See Migration.prepare\n\n \nstatic\n \nfunc\n \nprepare\n(\non\n \nconnection\n:\n \nMySQLConnection\n)\n \n-\n \nFuture\nVoid\n \n{\n\n \nreturn\n \nconnection\n.\ncreate\n(\nself\n)\n \n{\n \nbuilder\n \nin\n\n \ntry\n \nbuilder\n.\nfield\n(\nfor\n:\n \n\\\n.\nid\n)\n\n \ntry\n \nbuilder\n.\nfield\n(\nfor\n:\n \n\\\n.\nname\n)\n\n \ntry\n \nbuilder\n.\nfield\n(\nfor\n:\n \n\\\n.\nage\n)\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nWe pass \nself\n (shorthand for \nUser.self\n since this is a static method) as the first argument to the \n.create\n method. This indicates\nto Fluent that we would like to create a schema for the \nUser\n model.\n\n\nNext, we pass a closure that accepts a \nSchemaBuilder\n for our \nUser\n model.\nWe can then call \n.field\non this builder to describe what fields we'd like our table to have.\n\n\nSince we are passing key paths to our \nUser\n model (indicated by \n\\.\n), Fluent can see what type those properties are.\nFor most common types (\nString\n, \nInt\n, \nDouble\n, etc) Fluent will automatically be able to determine the best\ndatabase field type to use.\n\n\nYou can also choose to manually select which database field type to use for a given field.\n\n\ntry\n \nbuilder\n.\nfield\n(\ntype\n:\n \n.\ntext\n,\n \nfor\n:\n \n\\\n.\nname\n)\n\n\n\n\n\n\nLearn more about creating, updating, and deleting schemas in \nFluent \n Schema Builder\n.\n\n\nRevert\n\n\nRevert is the opposite of prepare. Its job is to undo anything that was done in prepare. It is used when you boot your \napp with the \n--revert\n option. \n\n\nTo implement \nrevert\n for our model, we simply use \n.delete\n to indicate that we would like to delete the schema created for \nUser\n.\n\n\nextension\n \nUser\n:\n \nMigration\n \n{\n\n \n/// See Migration.revert\n\n \nstatic\n \nfunc\n \nrevert\n(\non\n \nconnection\n:\n \nMySQLConnection\n)\n \n-\n \nFuture\nVoid\n \n{\n\n \nreturn\n \nconnection\n.\ndelete\n(\nself\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nExample\n\n\nWe now have a fully functioning model with migration!\n\n\nextension\n \nTestUser\n:\n \nMigration\n \n{\n\n \n/// See Migration.prepare\n\n \nstatic\n \nfunc\n \nprepare\n(\non\n \nconnection\n:\n \nSQLiteConnection\n)\n \n-\n \nFuture\nVoid\n \n{\n\n \nreturn\n \nconnection\n.\ncreate\n(\nself\n)\n \n{\n \nbuilder\n \nin\n\n \ntry\n \nbuilder\n.\nfield\n(\nfor\n:\n \n\\\n.\nid\n)\n\n \ntry\n \nbuilder\n.\nfield\n(\nfor\n:\n \n\\\n.\nname\n)\n\n \ntry\n \nbuilder\n.\nfield\n(\nfor\n:\n \n\\\n.\nage\n)\n\n \n}\n\n \n}\n\n\n \n/// See Migration.revert\n\n \nstatic\n \nfunc\n \nrevert\n(\non\n \nconnection\n:\n \nSQLiteConnection\n)\n \n-\n \nFuture\nVoid\n \n{\n\n \nreturn\n \nconnection\n.\ndelete\n(\nself\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nDone\n\n\nNow that you have a working Fluent model and migration, you can move onto \nquerying\n your model.",
|
|
"title": "Migrations"
|
|
},
|
|
{
|
|
"location": "/fluent/migrations/#getting-started-with-migrations",
|
|
"text": "Migrations are a way of making organized, testable, and reliable changes to your database's structure--\neven while it's in production! Migrations are often used for preparing a database schema for your models. However, they can also be used to \nmake normal queries to your database. In this guide we will cover creating both types of migrations.",
|
|
"title": "Getting Started with Migrations"
|
|
},
|
|
{
|
|
"location": "/fluent/migrations/#model-schema",
|
|
"text": "Let's take a look at how we can prepare a schema supporting database to accept the User model from the previous section . Just like we did with the Model protocol, we will conform our User to Migration . import Fluent extension User : Migration { } Swift will inform us that User does not yet conform. Let's add the required methods!",
|
|
"title": "Model Schema"
|
|
},
|
|
{
|
|
"location": "/fluent/migrations/#prepare",
|
|
"text": "The first method to implement is prepare . This method is where you make any of your \ndesired changes to the database. For our User model, we simply want to create a table that can store one or more users. To do this,\nwe will use the .create(...) function on the supplied database connection. extension User : Migration { \n /// See Migration.prepare \n static func prepare ( on connection : MySQLConnection ) - Future Void { \n return connection . create ( self ) { builder in \n try builder . field ( for : \\ . id ) \n try builder . field ( for : \\ . name ) \n try builder . field ( for : \\ . age ) \n } \n } } We pass self (shorthand for User.self since this is a static method) as the first argument to the .create method. This indicates\nto Fluent that we would like to create a schema for the User model. Next, we pass a closure that accepts a SchemaBuilder for our User model.\nWe can then call .field on this builder to describe what fields we'd like our table to have. Since we are passing key paths to our User model (indicated by \\. ), Fluent can see what type those properties are.\nFor most common types ( String , Int , Double , etc) Fluent will automatically be able to determine the best\ndatabase field type to use. You can also choose to manually select which database field type to use for a given field. try builder . field ( type : . text , for : \\ . name ) Learn more about creating, updating, and deleting schemas in Fluent Schema Builder .",
|
|
"title": "Prepare"
|
|
},
|
|
{
|
|
"location": "/fluent/migrations/#revert",
|
|
"text": "Revert is the opposite of prepare. Its job is to undo anything that was done in prepare. It is used when you boot your \napp with the --revert option. To implement revert for our model, we simply use .delete to indicate that we would like to delete the schema created for User . extension User : Migration { \n /// See Migration.revert \n static func revert ( on connection : MySQLConnection ) - Future Void { \n return connection . delete ( self ) \n } }",
|
|
"title": "Revert"
|
|
},
|
|
{
|
|
"location": "/fluent/migrations/#example",
|
|
"text": "We now have a fully functioning model with migration! extension TestUser : Migration { \n /// See Migration.prepare \n static func prepare ( on connection : SQLiteConnection ) - Future Void { \n return connection . create ( self ) { builder in \n try builder . field ( for : \\ . id ) \n try builder . field ( for : \\ . name ) \n try builder . field ( for : \\ . age ) \n } \n } \n\n /// See Migration.revert \n static func revert ( on connection : SQLiteConnection ) - Future Void { \n return connection . delete ( self ) \n } }",
|
|
"title": "Example"
|
|
},
|
|
{
|
|
"location": "/fluent/migrations/#done",
|
|
"text": "Now that you have a working Fluent model and migration, you can move onto querying your model.",
|
|
"title": "Done"
|
|
},
|
|
{
|
|
"location": "/fluent/querying/",
|
|
"text": "Querying Models\n\n\nOnce you have a \nmodel\n (and optionally a \nmigration\n) you can start\nquerying your database to create, read, update, and delete data.\n\n\nConnection\n\n\nThe first thing you need to query your database, is a connection to it. Luckily, they are easy to get.\n\n\nYou can use either the application or an incoming request to create a database connection. You just need\naccess to the database identifier.\n\n\nRequest\n\n\nThe preferred method for getting access to a database connection is via an incoming request.\n\n\nrouter\n.\nget\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \nin\n\n \n// use the db here\n\n \n}\n\n\n}\n\n\n\n\n\n\nThe first parameter is the database's identifier. The second parameter is a closure\nthat accepts a connection to that database.\n\n\n\n\nTip\n\n\nAlthough the closure to \n.withConnection(to: ...)\n accepts a database \nconnection\n, we often use just \ndb\n for short.\n\n\n\n\nThe closure is expected to return a \nFuture\nVoid\n. When this future is completed, the connection will be released\nback into Fluent's connection pool. This is usually acheived by simply returning the query as we will soon see.\n\n\nApplication\n\n\nYou can also create a database connection using the application. This is useful for cases where you must access\nthe database from outside a request/response event.\n\n\nlet\n \nres\n \n=\n \napp\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \nin\n\n \n// use the db here\n\n\n}\n\n\nprint\n(\nres\n)\n \n// Future\nT\n\n\n\n\n\n\nThis is usually done in the \nboot section\n of your application.\n\n\n\n\nWarning\n\n\nDo not use database connections created by the application in a route closure (when responding to a request).\nAlways use the incoming request to create a connection to avoid threading issues.\n\n\n\n\nCreate\n\n\nTo create (save) a model to the database, first initialize an instance of your model, then call \n.save(on: )\n.\n\n\nrouter\n.\npost\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \n-\n \nFuture\nUser\n \nin\n\n \nlet\n \nuser\n \n=\n \nUser\n(\nname\n:\n \nVapor\n,\n \nage\n:\n \n3\n)\n\n \nreturn\n \nuser\n.\nsave\n(\non\n:\n \ndb\n).\ntransform\n(\nto\n:\n \nuser\n)\n \n// Future\nUser\n\n \n}\n\n\n}\n\n\n\n\n\n\nResponse\n\n\n.save(on: )\n returns a \nFuture\nVoid\n that completes when the user has finished saving. In this example, we then\nmap that \nFuture\nVoid\n to a \nFuture\nUser\n by calling \n.map\n and passing in the recently-saved user.\n\n\nYou can also use \n.map\n to return a simple success response.\n\n\nrouter\n.\npost\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \n-\n \nFuture\nHTTPResponse\n \nin\n\n \nlet\n \nuser\n \n=\n \nUser\n(\nname\n:\n \nVapor\n,\n \nage\n:\n \n3\n)\n\n \nreturn\n \nuser\n.\nsave\n(\non\n:\n \ndb\n).\nmap\n(\nto\n:\n \nHTTPResponse\n.\nself\n)\n \n{\n\n \nreturn\n \nHTTPResponse\n(\nstatus\n:\n \n.\ncreated\n)\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nMultiple\n\n\nIf you have multiple instances to save, do so using an array. Arrays containing only futures behave like futures.\n\n\nrouter\n.\npost\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \n-\n \nFuture\nHTTPResponse\n \nin\n\n \nlet\n \nmarie\n \n=\n \nUser\n(\nname\n:\n \nMarie Curie\n,\n \nage\n:\n \n66\n)\n\n \nlet\n \ncharles\n \n=\n \nUser\n(\nname\n:\n \nCharles Darwin\n,\n \nage\n:\n \n73\n)\n\n \nreturn\n \n[\n\n \nmarie\n.\nsave\n(\non\n:\n \ndb\n),\n\n \ncharles\n.\nsave\n(\non\n:\n \ndb\n)\n\n \n].\nmap\n(\nto\n:\n \nHTTPResponse\n.\nself\n)\n \n{\n\n \nreturn\n \nHTTPResponse\n(\nstatus\n:\n \n.\ncreated\n)\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nRead\n\n\nTo read models from the database, use \n.query()\n on the database connection to create a \nQueryBuilder\n.\n\n\nAll\n\n\nFetch all instances of a model from the database using \n.all()\n.\n\n\nrouter\n.\nget\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \n-\n \nFuture\n[\nUser\n]\n \nin\n\n \nreturn\n \ndb\n.\nquery\n(\nUser\n.\nself\n).\nall\n()\n\n \n}\n\n\n}\n\n\n\n\n\n\nFilter\n\n\nUse \n.filter(...)\n to apply \nfilters\n to your query.\n\n\nrouter\n.\nget\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \n-\n \nFuture\n[\nUser\n]\n \nin\n\n \nreturn\n \ntry\n \ndb\n.\nquery\n(\nUser\n.\nself\n).\nfilter\n(\n\\\nUser\n.\nage\n \n \n50\n).\nall\n()\n\n \n}\n\n\n}\n\n\n\n\n\n\nFirst\n\n\nYou can also use \n.first()\n to just get the first result.\n\n\nrouter\n.\nget\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \n-\n \nFuture\nUser\n \nin\n\n \nreturn\n \ntry\n \ndb\n.\nquery\n(\nUser\n.\nself\n).\nfilter\n(\n\\\nUser\n.\nname\n \n==\n \nVapor\n).\nfirst\n().\nmap\n(\nto\n:\n \nUser\n.\nself\n)\n \n{\n \nuser\n \nin\n\n \nguard\n \nlet\n \nuser\n \n=\n \nuser\n \nelse\n \n{\n\n \nthrow\n \nAbort\n(.\nnotFound\n,\n \nreason\n:\n \nCould not find user.\n)\n\n \n}\n\n\n \nreturn\n \nuser\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nNotice we use \n.map(to:)\n here to convert the optional user returned by \n.first()\n to a non-optional\nuser, or we throw an error.\n\n\nUpdate\n\n\nrouter\n.\nput\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \n-\n \nFuture\nUser\n \nin\n\n \nreturn\n \ndb\n.\nquery\n(\nUser\n.\nself\n).\nfirst\n().\nmap\n(\nto\n:\n \nUser\n.\nself\n)\n \n{\n \nuser\n \nin\n\n \nguard\n \nlet\n \nuser\n \n=\n \n$0\n \nelse\n \n{\n\n \nthrow\n \nAbort\n(.\nnotFound\n,\n \nreason\n:\n \nCould not find user.\n)\n\n \n}\n\n\n \nreturn\n \nuser\n\n \n}.\nflatMap\n(\nto\n:\n \nUser\n.\nself\n)\n \n{\n \nuser\n \nin\n\n \nuser\n.\nage\n \n+=\n \n1\n\n \nreturn\n \nuser\n.\nupdate\n(\non\n:\n \ndb\n).\nmap\n(\nto\n:\n \nUser\n.\nself\n)\n \n{\n \nuser\n \n}\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nNotice we use \n.map(to:)\n here to convert the optional user returned by \n.first()\n to a non-optional\nuser, or we throw an error.\n\n\nDelete\n\n\nrouter\n.\ndelete\n(...)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nfoo\n)\n \n{\n \ndb\n \n-\n \nFuture\nUser\n \nin\n\n \nreturn\n \ndb\n.\nquery\n(\nUser\n.\nself\n).\nfirst\n().\nmap\n(\nto\n:\n \nUser\n.\nself\n)\n \n{\n \nuser\n \nin\n\n \nguard\n \nlet\n \nuser\n \n=\n \n$0\n \nelse\n \n{\n\n \nthrow\n \nAbort\n(.\nnotFound\n,\n \nreason\n:\n \nCould not find user.\n)\n\n \n}\n\n \nreturn\n \nuser\n\n \n}.\nflatMap\n(\nto\n:\n \nUser\n.\nself\n)\n \n{\n \nuser\n \nin\n\n \nreturn\n \nuser\n.\ndelete\n(\non\n:\n \ndb\n).\ntransfom\n(\nto\n:\n \nuser\n)\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nNotice we use \n.map(to:)\n here to convert the optional user returned by \n.first()\n to a non-optional\nuser, or we throw an error.",
|
|
"title": "Querying"
|
|
},
|
|
{
|
|
"location": "/fluent/querying/#querying-models",
|
|
"text": "Once you have a model (and optionally a migration ) you can start\nquerying your database to create, read, update, and delete data.",
|
|
"title": "Querying Models"
|
|
},
|
|
{
|
|
"location": "/fluent/querying/#connection",
|
|
"text": "The first thing you need to query your database, is a connection to it. Luckily, they are easy to get. You can use either the application or an incoming request to create a database connection. You just need\naccess to the database identifier.",
|
|
"title": "Connection"
|
|
},
|
|
{
|
|
"location": "/fluent/querying/#request",
|
|
"text": "The preferred method for getting access to a database connection is via an incoming request. router . get (...) { req in \n return req . withConnection ( to : . foo ) { db in \n // use the db here \n } } The first parameter is the database's identifier. The second parameter is a closure\nthat accepts a connection to that database. Tip Although the closure to .withConnection(to: ...) accepts a database connection , we often use just db for short. The closure is expected to return a Future Void . When this future is completed, the connection will be released\nback into Fluent's connection pool. This is usually acheived by simply returning the query as we will soon see.",
|
|
"title": "Request"
|
|
},
|
|
{
|
|
"location": "/fluent/querying/#application",
|
|
"text": "You can also create a database connection using the application. This is useful for cases where you must access\nthe database from outside a request/response event. let res = app . withConnection ( to : . foo ) { db in \n // use the db here } print ( res ) // Future T This is usually done in the boot section of your application. Warning Do not use database connections created by the application in a route closure (when responding to a request).\nAlways use the incoming request to create a connection to avoid threading issues.",
|
|
"title": "Application"
|
|
},
|
|
{
|
|
"location": "/fluent/querying/#create",
|
|
"text": "To create (save) a model to the database, first initialize an instance of your model, then call .save(on: ) . router . post (...) { req in \n return req . withConnection ( to : . foo ) { db - Future User in \n let user = User ( name : Vapor , age : 3 ) \n return user . save ( on : db ). transform ( to : user ) // Future User \n } }",
|
|
"title": "Create"
|
|
},
|
|
{
|
|
"location": "/fluent/querying/#response",
|
|
"text": ".save(on: ) returns a Future Void that completes when the user has finished saving. In this example, we then\nmap that Future Void to a Future User by calling .map and passing in the recently-saved user. You can also use .map to return a simple success response. router . post (...) { req in \n return req . withConnection ( to : . foo ) { db - Future HTTPResponse in \n let user = User ( name : Vapor , age : 3 ) \n return user . save ( on : db ). map ( to : HTTPResponse . self ) { \n return HTTPResponse ( status : . created ) \n } \n } }",
|
|
"title": "Response"
|
|
},
|
|
{
|
|
"location": "/fluent/querying/#multiple",
|
|
"text": "If you have multiple instances to save, do so using an array. Arrays containing only futures behave like futures. router . post (...) { req in \n return req . withConnection ( to : . foo ) { db - Future HTTPResponse in \n let marie = User ( name : Marie Curie , age : 66 ) \n let charles = User ( name : Charles Darwin , age : 73 ) \n return [ \n marie . save ( on : db ), \n charles . save ( on : db ) \n ]. map ( to : HTTPResponse . self ) { \n return HTTPResponse ( status : . created ) \n } \n } }",
|
|
"title": "Multiple"
|
|
},
|
|
{
|
|
"location": "/fluent/querying/#read",
|
|
"text": "To read models from the database, use .query() on the database connection to create a QueryBuilder .",
|
|
"title": "Read"
|
|
},
|
|
{
|
|
"location": "/fluent/querying/#all",
|
|
"text": "Fetch all instances of a model from the database using .all() . router . get (...) { req in \n return req . withConnection ( to : . foo ) { db - Future [ User ] in \n return db . query ( User . self ). all () \n } }",
|
|
"title": "All"
|
|
},
|
|
{
|
|
"location": "/fluent/querying/#filter",
|
|
"text": "Use .filter(...) to apply filters to your query. router . get (...) { req in \n return req . withConnection ( to : . foo ) { db - Future [ User ] in \n return try db . query ( User . self ). filter ( \\ User . age 50 ). all () \n } }",
|
|
"title": "Filter"
|
|
},
|
|
{
|
|
"location": "/fluent/querying/#first",
|
|
"text": "You can also use .first() to just get the first result. router . get (...) { req in \n return req . withConnection ( to : . foo ) { db - Future User in \n return try db . query ( User . self ). filter ( \\ User . name == Vapor ). first (). map ( to : User . self ) { user in \n guard let user = user else { \n throw Abort (. notFound , reason : Could not find user. ) \n } \n\n return user \n } \n } } Notice we use .map(to:) here to convert the optional user returned by .first() to a non-optional\nuser, or we throw an error.",
|
|
"title": "First"
|
|
},
|
|
{
|
|
"location": "/fluent/querying/#update",
|
|
"text": "router . put (...) { req in \n return req . withConnection ( to : . foo ) { db - Future User in \n return db . query ( User . self ). first (). map ( to : User . self ) { user in \n guard let user = $0 else { \n throw Abort (. notFound , reason : Could not find user. ) \n } \n\n return user \n }. flatMap ( to : User . self ) { user in \n user . age += 1 \n return user . update ( on : db ). map ( to : User . self ) { user } \n } \n } } Notice we use .map(to:) here to convert the optional user returned by .first() to a non-optional\nuser, or we throw an error.",
|
|
"title": "Update"
|
|
},
|
|
{
|
|
"location": "/fluent/querying/#delete",
|
|
"text": "router . delete (...) { req in \n return req . withConnection ( to : . foo ) { db - Future User in \n return db . query ( User . self ). first (). map ( to : User . self ) { user in \n guard let user = $0 else { \n throw Abort (. notFound , reason : Could not find user. ) \n } \n return user \n }. flatMap ( to : User . self ) { user in \n return user . delete ( on : db ). transfom ( to : user ) \n } \n } } Notice we use .map(to:) here to convert the optional user returned by .first() to a non-optional\nuser, or we throw an error.",
|
|
"title": "Delete"
|
|
},
|
|
{
|
|
"location": "/fluent/query-builder/",
|
|
"text": "Fluent Query Builder\n\n\nComing soon.\n\n\nFilter\n\n\nComing soon.\n\n\nCompare\n\n\nComing soon.\n\n\nGroup\n\n\nComing soon.\n\n\nSubset\n\n\nComing soon.\n\n\nJoin\n\n\nComing soon.\n\n\nRange\n\n\nComing soon.\n\n\nSort\n\n\nComing soon.\n\n\nExecute\n\n\nComing soon.\n\n\nAggregate\n\n\nComing soon.\n\n\nAll\n\n\nComing soon.\n\n\nFirst\n\n\nComing soon.\n\n\nQuery\n\n\nComing soon.",
|
|
"title": "Query Builder"
|
|
},
|
|
{
|
|
"location": "/fluent/query-builder/#fluent-query-builder",
|
|
"text": "Coming soon.",
|
|
"title": "Fluent Query Builder"
|
|
},
|
|
{
|
|
"location": "/fluent/query-builder/#filter",
|
|
"text": "Coming soon.",
|
|
"title": "Filter"
|
|
},
|
|
{
|
|
"location": "/fluent/query-builder/#compare",
|
|
"text": "Coming soon.",
|
|
"title": "Compare"
|
|
},
|
|
{
|
|
"location": "/fluent/query-builder/#group",
|
|
"text": "Coming soon.",
|
|
"title": "Group"
|
|
},
|
|
{
|
|
"location": "/fluent/query-builder/#subset",
|
|
"text": "Coming soon.",
|
|
"title": "Subset"
|
|
},
|
|
{
|
|
"location": "/fluent/query-builder/#join",
|
|
"text": "Coming soon.",
|
|
"title": "Join"
|
|
},
|
|
{
|
|
"location": "/fluent/query-builder/#range",
|
|
"text": "Coming soon.",
|
|
"title": "Range"
|
|
},
|
|
{
|
|
"location": "/fluent/query-builder/#sort",
|
|
"text": "Coming soon.",
|
|
"title": "Sort"
|
|
},
|
|
{
|
|
"location": "/fluent/query-builder/#execute",
|
|
"text": "Coming soon.",
|
|
"title": "Execute"
|
|
},
|
|
{
|
|
"location": "/fluent/query-builder/#aggregate",
|
|
"text": "Coming soon.",
|
|
"title": "Aggregate"
|
|
},
|
|
{
|
|
"location": "/fluent/query-builder/#all",
|
|
"text": "Coming soon.",
|
|
"title": "All"
|
|
},
|
|
{
|
|
"location": "/fluent/query-builder/#first",
|
|
"text": "Coming soon.",
|
|
"title": "First"
|
|
},
|
|
{
|
|
"location": "/fluent/query-builder/#query",
|
|
"text": "Coming soon.",
|
|
"title": "Query"
|
|
},
|
|
{
|
|
"location": "/fluent/schema-builder/",
|
|
"text": "Fluent Schema Builder\n\n\nComing soon.\n\n\nCreate\n\n\nComing soon.\n\n\nUpdate\n\n\nComing soon.\n\n\nDelete\n\n\nComing soon.\n\n\nReferences\n\n\nComing soon.",
|
|
"title": "Schema Builder"
|
|
},
|
|
{
|
|
"location": "/fluent/schema-builder/#fluent-schema-builder",
|
|
"text": "Coming soon.",
|
|
"title": "Fluent Schema Builder"
|
|
},
|
|
{
|
|
"location": "/fluent/schema-builder/#create",
|
|
"text": "Coming soon.",
|
|
"title": "Create"
|
|
},
|
|
{
|
|
"location": "/fluent/schema-builder/#update",
|
|
"text": "Coming soon.",
|
|
"title": "Update"
|
|
},
|
|
{
|
|
"location": "/fluent/schema-builder/#delete",
|
|
"text": "Coming soon.",
|
|
"title": "Delete"
|
|
},
|
|
{
|
|
"location": "/fluent/schema-builder/#references",
|
|
"text": "Coming soon.",
|
|
"title": "References"
|
|
},
|
|
{
|
|
"location": "/fluent/relations/",
|
|
"text": "Fluent Relations\n\n\nComing soon.\n\n\nParent / Child\n\n\nComing soon.\n\n\nSiblings\n\n\nComing soon.",
|
|
"title": "Relations"
|
|
},
|
|
{
|
|
"location": "/fluent/relations/#fluent-relations",
|
|
"text": "Coming soon.",
|
|
"title": "Fluent Relations"
|
|
},
|
|
{
|
|
"location": "/fluent/relations/#parent-child",
|
|
"text": "Coming soon.",
|
|
"title": "Parent / Child"
|
|
},
|
|
{
|
|
"location": "/fluent/relations/#siblings",
|
|
"text": "Coming soon.",
|
|
"title": "Siblings"
|
|
},
|
|
{
|
|
"location": "/fluent/pivot/",
|
|
"text": "Fluent Pivot\n\n\nComing soon.",
|
|
"title": "Pivot"
|
|
},
|
|
{
|
|
"location": "/fluent/pivot/#fluent-pivot",
|
|
"text": "Coming soon.",
|
|
"title": "Fluent Pivot"
|
|
},
|
|
{
|
|
"location": "/fluent/transaction/",
|
|
"text": "Fluent Transactions\n\n\nComing soon.",
|
|
"title": "Transaction"
|
|
},
|
|
{
|
|
"location": "/fluent/transaction/#fluent-transactions",
|
|
"text": "Coming soon.",
|
|
"title": "Fluent Transactions"
|
|
},
|
|
{
|
|
"location": "/fluent/database/",
|
|
"text": "Fluent Database\n\n\nComing soon.\n\n\nConnection\n\n\nComing soon.\n\n\nLogger\n\n\nComing soon.",
|
|
"title": "Database"
|
|
},
|
|
{
|
|
"location": "/fluent/database/#fluent-database",
|
|
"text": "Coming soon.",
|
|
"title": "Fluent Database"
|
|
},
|
|
{
|
|
"location": "/fluent/database/#connection",
|
|
"text": "Coming soon.",
|
|
"title": "Connection"
|
|
},
|
|
{
|
|
"location": "/fluent/database/#logger",
|
|
"text": "Coming soon.",
|
|
"title": "Logger"
|
|
},
|
|
{
|
|
"location": "/http/getting-started/",
|
|
"text": "Getting Started with HTTP\n\n\nHTTP (\nvapor/http\n) is a non-blocking, event-driven HTTP library built on SwiftNIO. It makes working with SwiftNIO's HTTP handlers easy and offers higher-level functionality like media types, client upgrading, streaming bodies, and more. Creating an HTTP echo server takes just a few lines of code.\n\n\n\n\nTip\n\n\nIf you use Vapor, most of HTTP's APIs will be wrapped by more convenient methods. Usually the only HTTP type you\nwill interact with is the \nhttp\n property of \nRequest\n or \nResponse\n.\n\n\n\n\nVapor\n\n\nThis package is included with Vapor and exported by default. You will have access to all \nHTTP\n APIs when you import \nVapor\n.\n\n\nimport\n \nVapor\n\n\n\n\n\n\nStandalone\n\n\nThe HTTP package is lightweight, pure Swift, and only depends on SwiftNIO. This means it can be used as an HTTP framework in any Swift project\neven one not using Vapor.\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\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/http.git\n,\n \nfrom\n:\n \n3.0.0\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nHTTP\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport HTTP\n to access the APIs.\n\n\nThe rest of this guide will give you an overview of what is available in the HTTP package. As always, feel free to visit the \nAPI docs\n for more in-depth information.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/http/getting-started/#getting-started-with-http",
|
|
"text": "HTTP ( vapor/http ) is a non-blocking, event-driven HTTP library built on SwiftNIO. It makes working with SwiftNIO's HTTP handlers easy and offers higher-level functionality like media types, client upgrading, streaming bodies, and more. Creating an HTTP echo server takes just a few lines of code. Tip If you use Vapor, most of HTTP's APIs will be wrapped by more convenient methods. Usually the only HTTP type you\nwill interact with is the http property of Request or Response .",
|
|
"title": "Getting Started with HTTP"
|
|
},
|
|
{
|
|
"location": "/http/getting-started/#vapor",
|
|
"text": "This package is included with Vapor and exported by default. You will have access to all HTTP APIs when you import Vapor . import Vapor",
|
|
"title": "Vapor"
|
|
},
|
|
{
|
|
"location": "/http/getting-started/#standalone",
|
|
"text": "The HTTP package is lightweight, pure Swift, and only depends on SwiftNIO. This means it can be used as an HTTP framework in any Swift project even one not using Vapor. To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . package ( url : https://github.com/vapor/http.git , from : 3.0.0 ), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ HTTP , ... ]) \n ] ) Use import HTTP to access the APIs. The rest of this guide will give you an overview of what is available in the HTTP package. As always, feel free to visit the API docs for more in-depth information.",
|
|
"title": "Standalone"
|
|
},
|
|
{
|
|
"location": "/http/client/",
|
|
"text": "Using HTTPClient\n\n\nHTTP clients send requests to remote HTTP servers which then generate and return responses. HTTP clients are usually only active for a matter of seconds to minutes and may send one or more requests. The \nHTTPClient\n type is what powers Vapor's higher-level client. This short guide will show you how to send HTTP requests to servers manually.\n\n\n\n\nTip\n\n\nIf you are using Vapor, you probably don't need to use HTTP's APIs directly. Refer to \nVapor \n Client\n for the more convenient APIs.\n\n\n\n\nFor this example, we will fetch Vapor's homepage. The first step is to create a connected HTTP client. Use the static \nconnect(...)\n method to do this.\n\n\n// Connect a new client to the supplied hostname.\n\n\nlet\n \nclient\n \n=\n \ntry\n \nHTTPClient\n.\nconnect\n(\nhostname\n:\n \nvapor.codes\n,\n \non\n:\n \n...).\nwait\n()\n\n\nprint\n(\nclient\n)\n \n// HTTPClient\n\n\n// Create an HTTP request: GET /\n\n\nlet\n \nhttpReq\n \n=\n \nHTTPRequest\n(\nmethod\n:\n \n.\nGET\n,\n \nurl\n:\n \n/\n)\n\n\n// Send the HTTP request, fetching a response\n\n\nlet\n \nhttpRes\n \n=\n \ntry\n \nclient\n.\nsend\n(\nhttpReq\n).\nwait\n()\n\n\nprint\n(\nhttpRes\n)\n \n// HTTPResponse\n\n\n\n\n\n\nTake note that we are passing the \nhostname\n. This is different from a full URL. You can use \nURL\n and \nURLComponents\n from Foundation to parse out a hostname. Vapor's convenience APIs do this automatically.\n\n\n\n\nWarning\n\n\nThis guide assumes you are on the main thread. Don't use \nwait()\n if you are inside of a route closure. See \nAsync \n Overview\n for more information.\n\n\n\n\nAfter we have a connected HTTP client, we can send an \nHTTPRequest\n using \nsend(...)\n. This will return an \nHTTPResponse\n containing the headers and body sent back from the server. See \nHTTP \n Message\n for more information on HTTP messages. \n\n\nAPI Docs\n\n\nThat's it! Congratulations on making your first HTTP request. Check out the \nAPI docs\n for more in-depth information about all of the available parameters and methods.",
|
|
"title": "Client"
|
|
},
|
|
{
|
|
"location": "/http/client/#using-httpclient",
|
|
"text": "HTTP clients send requests to remote HTTP servers which then generate and return responses. HTTP clients are usually only active for a matter of seconds to minutes and may send one or more requests. The HTTPClient type is what powers Vapor's higher-level client. This short guide will show you how to send HTTP requests to servers manually. Tip If you are using Vapor, you probably don't need to use HTTP's APIs directly. Refer to Vapor Client for the more convenient APIs. For this example, we will fetch Vapor's homepage. The first step is to create a connected HTTP client. Use the static connect(...) method to do this. // Connect a new client to the supplied hostname. let client = try HTTPClient . connect ( hostname : vapor.codes , on : ...). wait () print ( client ) // HTTPClient // Create an HTTP request: GET / let httpReq = HTTPRequest ( method : . GET , url : / ) // Send the HTTP request, fetching a response let httpRes = try client . send ( httpReq ). wait () print ( httpRes ) // HTTPResponse Take note that we are passing the hostname . This is different from a full URL. You can use URL and URLComponents from Foundation to parse out a hostname. Vapor's convenience APIs do this automatically. Warning This guide assumes you are on the main thread. Don't use wait() if you are inside of a route closure. See Async Overview for more information. After we have a connected HTTP client, we can send an HTTPRequest using send(...) . This will return an HTTPResponse containing the headers and body sent back from the server. See HTTP Message for more information on HTTP messages.",
|
|
"title": "Using HTTPClient"
|
|
},
|
|
{
|
|
"location": "/http/client/#api-docs",
|
|
"text": "That's it! Congratulations on making your first HTTP request. Check out the API docs for more in-depth information about all of the available parameters and methods.",
|
|
"title": "API Docs"
|
|
},
|
|
{
|
|
"location": "/http/server/",
|
|
"text": "Using HTTPServer\n\n\nHTTP servers respond to incoming \nHTTPRequests\n with \nHTTPResponses\n. The \nHTTPServer\n type is what powers Vapor's higher-level server. This short guide will show you how to set up your own HTTP server manually.\n\n\n\n\nTip\n\n\nIf you are using Vapor, you probably don't need to use HTTP's APIs directly. Refer to \nVapor \n Server\n for the more convenient APIs.\n\n\n\n\nResponder\n\n\nCreating an HTTP server is easy, and only takes a few lines of code. The first step is to create an \nHTTPServerResponder\n. This will be directly responsible for generating responses to incoming requests.\n\n\nLet's create a simple responder that will echo the request's content.\n\n\n/// Echoes the request as a response.\n\n\nstruct\n \nEchoResponder\n:\n \nHTTPServerResponder\n \n{\n\n \n/// See `HTTPServerResponder`.\n\n \nfunc\n \nrespond\n(\nto\n \nreq\n:\n \nHTTPRequest\n,\n \non\n \nworker\n:\n \nWorker\n)\n \n-\n \nFuture\nHTTPResponse\n \n{\n\n \n// Create an HTTPResponse with the same body as the HTTPRequest\n\n \nlet\n \nres\n \n=\n \nHTTPResponse\n(\nbody\n:\n \nreq\n.\nbody\n)\n\n \n// We don\nt need to do any async work here, we can just\n\n \n// se the Worker\ns event-loop to create a succeeded future.\n\n \nreturn\n \nworker\n.\neventLoop\n.\nnewSucceededFuture\n(\nresult\n:\n \nres\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nStart\n\n\nNow that we have a responder, we can create our \nHTTPServer\n. We just need to choose a hostname and port for the server to bind to. In this example, we will bind to \nhttp://localhost:8123\n.\n\n\n// Create an EventLoopGroup with an appropriate number\n\n\n// of threads for the system we are running on.\n\n\nlet\n \ngroup\n \n=\n \nMultiThreadedEventLoopGroup\n(\nnumThreads\n:\n \nSystem\n.\ncoreCount\n)\n\n\n// Make sure to shutdown the group when the application exits.\n\n\ndefer\n \n{\n \ntry\n!\n \ngroup\n.\nsyncShutdownGracefully\n()\n \n}\n\n\n\n// Start an HTTPServer using our EchoResponder\n\n\n// We are fine to use `wait()` here since we are on the main thread.\n\n\nlet\n \nserver\n \n=\n \ntry\n \nHTTPServer\n.\nstart\n(\n\n \nhostname\n:\n \nlocalhost\n,\n \n \nport\n:\n \n8123\n,\n \n \nresponder\n:\n \nEchoResponder\n(),\n \n \non\n:\n \ngroup\n\n\n).\nwait\n()\n\n\n\n// Wait for the server to close (indefinitely).\n\n\ntry\n \nserver\n.\nonClose\n.\nwait\n()\n\n\n\n\n\n\nThe static \nstart(...)\n method creates and returns a new \nHTTPServer\n asynchronously. The future will be completed when the server has finished booting succesfully, or it will contain an error if something went wrong.\n\n\nOnce the start future is complete, our server is running. By waiting for the server's \nonClose\n future to complete, we can keep our application alive until the server closes. Normally the server will not close itself--it will just run indefinitely. However if \nserver.close()\n is ever called, the application can exit gracefully.\n\n\nAPI Docs\n\n\nThat's it! Congratulations on making your first HTTP server and responder. Check out the \nAPI docs\n for more in-depth information about all of the available parameters and methods.",
|
|
"title": "Server"
|
|
},
|
|
{
|
|
"location": "/http/server/#using-httpserver",
|
|
"text": "HTTP servers respond to incoming HTTPRequests with HTTPResponses . The HTTPServer type is what powers Vapor's higher-level server. This short guide will show you how to set up your own HTTP server manually. Tip If you are using Vapor, you probably don't need to use HTTP's APIs directly. Refer to Vapor Server for the more convenient APIs.",
|
|
"title": "Using HTTPServer"
|
|
},
|
|
{
|
|
"location": "/http/server/#responder",
|
|
"text": "Creating an HTTP server is easy, and only takes a few lines of code. The first step is to create an HTTPServerResponder . This will be directly responsible for generating responses to incoming requests. Let's create a simple responder that will echo the request's content. /// Echoes the request as a response. struct EchoResponder : HTTPServerResponder { \n /// See `HTTPServerResponder`. \n func respond ( to req : HTTPRequest , on worker : Worker ) - Future HTTPResponse { \n // Create an HTTPResponse with the same body as the HTTPRequest \n let res = HTTPResponse ( body : req . body ) \n // We don t need to do any async work here, we can just \n // se the Worker s event-loop to create a succeeded future. \n return worker . eventLoop . newSucceededFuture ( result : res ) \n } }",
|
|
"title": "Responder"
|
|
},
|
|
{
|
|
"location": "/http/server/#start",
|
|
"text": "Now that we have a responder, we can create our HTTPServer . We just need to choose a hostname and port for the server to bind to. In this example, we will bind to http://localhost:8123 . // Create an EventLoopGroup with an appropriate number // of threads for the system we are running on. let group = MultiThreadedEventLoopGroup ( numThreads : System . coreCount ) // Make sure to shutdown the group when the application exits. defer { try ! group . syncShutdownGracefully () } // Start an HTTPServer using our EchoResponder // We are fine to use `wait()` here since we are on the main thread. let server = try HTTPServer . start ( \n hostname : localhost , \n port : 8123 , \n responder : EchoResponder (), \n on : group ). wait () // Wait for the server to close (indefinitely). try server . onClose . wait () The static start(...) method creates and returns a new HTTPServer asynchronously. The future will be completed when the server has finished booting succesfully, or it will contain an error if something went wrong. Once the start future is complete, our server is running. By waiting for the server's onClose future to complete, we can keep our application alive until the server closes. Normally the server will not close itself--it will just run indefinitely. However if server.close() is ever called, the application can exit gracefully.",
|
|
"title": "Start"
|
|
},
|
|
{
|
|
"location": "/http/server/#api-docs",
|
|
"text": "That's it! Congratulations on making your first HTTP server and responder. Check out the API docs for more in-depth information about all of the available parameters and methods.",
|
|
"title": "API Docs"
|
|
},
|
|
{
|
|
"location": "/http/message/",
|
|
"text": "Using HTTP Message\n\n\nThere are two types of HTTP messages, \nHTTPRequest\n and \nHTTPResponse\n. For the most part they are very similar, but there are a couple of differences. \n\n\nRequest\n\n\nHTTP requests are sent by clients to a server and they should always receive exactly one HTTP response. HTTP requests contain two unique fields over a standard HTTP message:\n\n\n\n\nmethod\n\n\nurl\n\n\n\n\nThe method and URL define what content on the server is being requested.\n\n\n/// GET /hello\n\n\nlet\n \nhttpReq\n \n=\n \nHTTPRequest\n(\nmethod\n:\n \n.\nGET\n,\n \nurl\n:\n \n/hello\n)\n\n\n\n\n\n\nYou can define these when initializing an HTTP request, or set them later if the request is mutable.\n\n\nvar\n \nhttpReq\n:\n \nHTTPRequest\n \n=\n \n...\n\n\nhttpReq\n.\nmethod\n \n=\n \n.\nPOST\n\n\nhttpReq\n.\nurl\n \n=\n \nURL\n(...)\n\n\n\n\n\n\nYou can use Foundation's \nURLComponents\n to create \nURL\ns from their base components. HTTP request also has a property \nurlString\n that you can use to set a custom URL \nString\n manually, without going through \nURL\n.\n\n\nHere is what a serialized HTTP request looks like. This one is querying \n/hello\n.\n\n\nGET\n \n/hello\n \nHTTP\n/\n1.1\n\n\nContent-Length\n:\n \n0\n\n\n\n\n\n\nResponse\n\n\nHTTP responses are generated by servers in response to an HTTP request. HTTP response only has one unique field over general HTTP messages:\n\n\n\n\nstatus\n\n\n\n\nThe HTTP status is used to inform the client of any errors. The status consists of a status code and a reason. The code is always a three digit number and the reason is a short string explaining the code. You can see all of the status codes on \nhttpstatuses.com\n.\n\n\nlet\n \nhttpRes\n \n=\n \nHTTPResponse\n(\nstatus\n:\n \n.\nok\n,\n \nbody\n:\n \nhello\n)\n\n\n\n\n\n\nAll of the commonly used HTTP statuses will have pre-defined values you can use, like \n.ok\n for \n200 OK\n. You can also define your own custom status codes.\n\n\nYou can define the status when initializing an HTTP response, or set it later if the response is mutable.\n\n\nvar\n \nhttpRes\n:\n \nHTTPResponse\n \n=\n \n...\n\n\nhttpRes\n.\nstatus\n \n=\n \n.\nnotFound\n\n\n\n\n\n\nHere is an example of a serialized HTTP response.\n\n\nHTTP\n/\n1.1\n \n200\n \nOK\n\n\nContent-Length\n:\n \n5\n\n\nContent-Type\n:\n \ntext/plain\n\n\nhello\n\n\n\n\n\nHeaders\n\n\nEvery HTTP message has a collection of headers. Headers contain metadata about the message and help to explain what is in the message's body. \n\n\nContent-Length: 5\n\n\nContent-Type: text/plain\n\n\n\n\n\n\nThere must be at least a \n\"Content-Length\"\n or \n\"Transfer-Encoding\"\n header to define how long the message's body is. There is almost always a \n\"Content-Type\"\n header that explains what \ntype\n of data the body contains. There are many other common headers such as \n\"Date\"\n which specifies when the message was created, and more.\n\n\nYou can access an HTTP message's headers using the \nheaders\n property.\n\n\nvar\n \nmessage\n:\n \nHTTPMessage\n \n...\n\n\nmessage\n.\nheaders\n.\nfirstValue\n(\nfor\n:\n \n.\ncontentLength\n)\n \n// 5\n\n\n\n\n\n\nIf you are interacting with common HTTP headers, you can use the convenience HTTP names instead of a raw \nString\n.\n\n\nBody\n\n\nHTTP messages can have an \nHTTPBody\n containing arbitrary data. This data can be either static or streaming and can be in whatever format you want. Use the \ncontentType\n header to describe the type of data.\n\n\nvar\n \nmessage\n:\n \nHTTPMessage\n \n=\n \n...\n\n\nmessage\n.\nbody\n \n=\n \nHTTPBody\n(\nstring\n:\n \nHello, world!\n)\n\n\nmessage\n.\ncontentType\n \n=\n \n.\nplainText\n\n\n\n\n\n\n\n\nTip\n\n\nSetting the \nbody\n property will automatically update the \n\"Content-Length\"\n or \n\"Transfer-Encoding\"\n headers if required.\n\n\n\n\nvar\n \nmessage\n:\n \nHTTPMessage\n \n=\n \n...\n\n\nmessage\n.\nbody\n \n=\n \nHTTPBody\n(\nstring\n:\n \n\n\n{\nmessage\n: \nHello\n,\n \nworld\n!\n}\n\n\n)\n\n\nmessage\n.\ncontentType\n \n=\n \n.\njson\n\n\n\n\n\n\nCodable\n\n\nTwo protocols are defined for making it easy to use \nCodable\n with HTTP:\n\n\n\n\nHTTPMessageEncoder\n\n\nHTTPMessageDecoder\n\n\n\n\nThese two coders allow you to encode and decode your custom \nCodable\n types into an HTTP body, setting the appropriate content type headers.\n\n\nBy default, HTTP provides conformance for \nJSONEncoder\n and \nJSONDecoder\n, but Vapor includes coders for many more types.\n\n\nHere is an example of encoding a \nCodable\n struct to an HTTP response.\n\n\nstruct\n \nGreeting\n:\n \nCodable\n \n{\n\n \nvar\n \nmessage\n:\n \nString\n\n\n}\n\n\n// Create an instance of Greeting\n\n\nlet\n \ngreeting\n \n=\n \nGreeting\n(\nmessage\n:\n \nHello, world!\n)\n\n\n// Create a 200 OK response\n\n\nvar\n \nhttpRes\n \n=\n \nHTTPResponse\n(\nstatus\n:\n \n.\nok\n)\n\n\n// Encode the greeting to the response\n\n\ntry\n \nJSONEncoder\n().\nencode\n(\ngreeting\n,\n \nto\n:\n \nhttpRes\n,\n \non\n:\n \n...)\n\n\n\n\n\n\nAPI Docs\n\n\nCheck out the \nAPI docs\n for more in-depth information about all of the methods.",
|
|
"title": "Message"
|
|
},
|
|
{
|
|
"location": "/http/message/#using-http-message",
|
|
"text": "There are two types of HTTP messages, HTTPRequest and HTTPResponse . For the most part they are very similar, but there are a couple of differences.",
|
|
"title": "Using HTTP Message"
|
|
},
|
|
{
|
|
"location": "/http/message/#request",
|
|
"text": "HTTP requests are sent by clients to a server and they should always receive exactly one HTTP response. HTTP requests contain two unique fields over a standard HTTP message: method url The method and URL define what content on the server is being requested. /// GET /hello let httpReq = HTTPRequest ( method : . GET , url : /hello ) You can define these when initializing an HTTP request, or set them later if the request is mutable. var httpReq : HTTPRequest = ... httpReq . method = . POST httpReq . url = URL (...) You can use Foundation's URLComponents to create URL s from their base components. HTTP request also has a property urlString that you can use to set a custom URL String manually, without going through URL . Here is what a serialized HTTP request looks like. This one is querying /hello . GET /hello HTTP / 1.1 Content-Length : 0",
|
|
"title": "Request"
|
|
},
|
|
{
|
|
"location": "/http/message/#response",
|
|
"text": "HTTP responses are generated by servers in response to an HTTP request. HTTP response only has one unique field over general HTTP messages: status The HTTP status is used to inform the client of any errors. The status consists of a status code and a reason. The code is always a three digit number and the reason is a short string explaining the code. You can see all of the status codes on httpstatuses.com . let httpRes = HTTPResponse ( status : . ok , body : hello ) All of the commonly used HTTP statuses will have pre-defined values you can use, like .ok for 200 OK . You can also define your own custom status codes. You can define the status when initializing an HTTP response, or set it later if the response is mutable. var httpRes : HTTPResponse = ... httpRes . status = . notFound Here is an example of a serialized HTTP response. HTTP / 1.1 200 OK Content-Length : 5 Content-Type : text/plain \n\nhello",
|
|
"title": "Response"
|
|
},
|
|
{
|
|
"location": "/http/message/#headers",
|
|
"text": "Every HTTP message has a collection of headers. Headers contain metadata about the message and help to explain what is in the message's body. Content-Length: 5 Content-Type: text/plain There must be at least a \"Content-Length\" or \"Transfer-Encoding\" header to define how long the message's body is. There is almost always a \"Content-Type\" header that explains what type of data the body contains. There are many other common headers such as \"Date\" which specifies when the message was created, and more. You can access an HTTP message's headers using the headers property. var message : HTTPMessage ... message . headers . firstValue ( for : . contentLength ) // 5 If you are interacting with common HTTP headers, you can use the convenience HTTP names instead of a raw String .",
|
|
"title": "Headers"
|
|
},
|
|
{
|
|
"location": "/http/message/#body",
|
|
"text": "HTTP messages can have an HTTPBody containing arbitrary data. This data can be either static or streaming and can be in whatever format you want. Use the contentType header to describe the type of data. var message : HTTPMessage = ... message . body = HTTPBody ( string : Hello, world! ) message . contentType = . plainText Tip Setting the body property will automatically update the \"Content-Length\" or \"Transfer-Encoding\" headers if required. var message : HTTPMessage = ... message . body = HTTPBody ( string : { message : Hello , world ! } ) message . contentType = . json",
|
|
"title": "Body"
|
|
},
|
|
{
|
|
"location": "/http/message/#codable",
|
|
"text": "Two protocols are defined for making it easy to use Codable with HTTP: HTTPMessageEncoder HTTPMessageDecoder These two coders allow you to encode and decode your custom Codable types into an HTTP body, setting the appropriate content type headers. By default, HTTP provides conformance for JSONEncoder and JSONDecoder , but Vapor includes coders for many more types. Here is an example of encoding a Codable struct to an HTTP response. struct Greeting : Codable { \n var message : String } // Create an instance of Greeting let greeting = Greeting ( message : Hello, world! ) // Create a 200 OK response var httpRes = HTTPResponse ( status : . ok ) // Encode the greeting to the response try JSONEncoder (). encode ( greeting , to : httpRes , on : ...)",
|
|
"title": "Codable"
|
|
},
|
|
{
|
|
"location": "/http/message/#api-docs",
|
|
"text": "Check out the API docs for more in-depth information about all of the methods.",
|
|
"title": "API Docs"
|
|
},
|
|
{
|
|
"location": "/leaf/getting-started/",
|
|
"text": "Warning\n\n\nLeaf 3.0 is still in beta. Some documentation may be missing or out of date.\n\n\n\n\nLeaf\n\n\nLeaf is a templating language that integrates with Futures, Reactive Streams and Codable. This section outlines how to import the Leaf package into a Vapor project.\n\n\nExample Folder Structure\n\n\nHello\n\u251c\u2500\u2500 Package.resolved\n\u251c\u2500\u2500 Package.swift\n\u251c\u2500\u2500 Public\n\u251c\u2500\u2500 Resources\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 Views\n\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 boot.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 configure.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 \u2514\u2500\u2500 AppTests.swift\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 LinuxMain.swift\n\u2514\u2500\u2500 LICENSE\n\n\n\n\n\nAdding Leaf to your project\n\n\nThe easiest way to use Leaf with Vapor is to include the Leaf repository as a dependency in Package.swift:\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\n\n\nlet\n \npackage\n \n=\n \nPackage\n(\n\n \nname\n:\n \nproject1\n,\n\n \ndependencies\n:\n \n[\n\n \n// \ud83d\udca7 A server-side Swift web framework.\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/vapor.git\n,\n \n.\nbranch\n(\nbeta\n)),\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/leaf.git\n,\n \n.\nbranch\n(\nbeta\n)),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\n\n \nname\n:\n \nApp\n,\n\n \ndependencies\n:\n \n[\nVapor\n,\n \nLeaf\n]\n\n \n),\n\n \n.\ntarget\n(\nname\n:\n \nRun\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n.\ntestTarget\n(\nname\n:\n \nAppTests\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n]\n\n\n)\n\n\n\n\n\n\nThe Leaf package adds Leaf to your project, but to configure it for use you must modify configure.swift:\n\n\n\n\nAdd \nimport Leaf\n to the top of the file so that Leaf is available to use. You will also need to add this to any file that will render templates.\n\n\nAdd \ntry services.register(LeafProvider())\n to the \nconfigure()\n function so that routes may render Leaf templates as needed.\n\n\n\n\nSyntax Highlighting\n\n\nYou may also wish to install one these third-party packages that provide support for syntax highlighting in Leaf templates.\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": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/leaf/getting-started/#leaf",
|
|
"text": "Leaf is a templating language that integrates with Futures, Reactive Streams and Codable. This section outlines how to import the Leaf package into a Vapor project.",
|
|
"title": "Leaf"
|
|
},
|
|
{
|
|
"location": "/leaf/getting-started/#example-folder-structure",
|
|
"text": "Hello\n\u251c\u2500\u2500 Package.resolved\n\u251c\u2500\u2500 Package.swift\n\u251c\u2500\u2500 Public\n\u251c\u2500\u2500 Resources\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 Views\n\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 boot.swift\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 configure.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 \u2514\u2500\u2500 AppTests.swift\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 LinuxMain.swift\n\u2514\u2500\u2500 LICENSE",
|
|
"title": "Example Folder Structure"
|
|
},
|
|
{
|
|
"location": "/leaf/getting-started/#adding-leaf-to-your-project",
|
|
"text": "The easiest way to use Leaf with Vapor is to include the Leaf repository as a dependency in Package.swift: // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : project1 , \n dependencies : [ \n // \ud83d\udca7 A server-side Swift web framework. \n . package ( url : https://github.com/vapor/vapor.git , . branch ( beta )), \n . package ( url : https://github.com/vapor/leaf.git , . branch ( beta )), \n ], \n targets : [ \n . target ( \n name : App , \n dependencies : [ Vapor , Leaf ] \n ), \n . target ( name : Run , dependencies : [ App ]), \n . testTarget ( name : AppTests , dependencies : [ App ]), \n ] ) The Leaf package adds Leaf to your project, but to configure it for use you must modify configure.swift: Add import Leaf to the top of the file so that Leaf is available to use. You will also need to add this to any file that will render templates. Add try services.register(LeafProvider()) to the configure() function so that routes may render Leaf templates as needed.",
|
|
"title": "Adding Leaf to your project"
|
|
},
|
|
{
|
|
"location": "/leaf/getting-started/#syntax-highlighting",
|
|
"text": "You may also wish to install one these third-party packages that provide support for syntax highlighting in Leaf templates.",
|
|
"title": "Syntax Highlighting"
|
|
},
|
|
{
|
|
"location": "/leaf/getting-started/#atom",
|
|
"text": "language-leaf by ButkiewiczP",
|
|
"title": "Atom"
|
|
},
|
|
{
|
|
"location": "/leaf/getting-started/#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/getting-started/#vs-code",
|
|
"text": "html-leaf by FranciscoAmado",
|
|
"title": "VS Code"
|
|
},
|
|
{
|
|
"location": "/leaf/getting-started/#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": "/leaf/basics/",
|
|
"text": "Basics\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 \u2013 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\nAsynchronous and reactive\n\n\n\n\nRendering a template\n\n\nOnce you have Leaf installed, you should create a directory called \u201cResources\u201d inside your project folder, and inside that create another directory called \u201cViews\u201d. This Resources/Views directory is the default location for Leaf templates, although you can change it if you want.\n\n\nFirstly, import Leaf to routes.swift\n\n\nimport\n \nLeaf\n\n\n\n\n\n\nThen, to render a basic Leaf template from a route, add this code:\n\n\nrouter\n.\nget\n \n{\n \nreq\n \n-\n \nFuture\nView\n \nin\n\n \nlet\n \nleaf\n \n=\n \ntry\n \nreq\n.\nmake\n(\nLeafRenderer\n.\nself\n)\n\n \nlet\n \ncontext\n \n=\n \n[\nString\n:\n \nString\n]()\n\n \nreturn\n \ntry\n \nleaf\n.\nrender\n(\nhome\n,\n \ncontext\n)\n\n\n}\n\n\n\n\n\n\nThat will load home.leaf in the Resources/Views directory and render it. The \ncontext\n dictionary is there to let you provide custom data to render inside the template, but you might find it easier to use codable structs instead because they provide extra type safety. For example:\n\n\nstruct\n \nHomePage\n:\n \nCodable\n \n{\n\n \nvar\n \ntitle\n:\n \nString\n\n \nvar\n \ncontent\n:\n \nString\n\n\n}\n\n\n\n\n\n\nAsync\n\n\nLeaf's engine is completely reactive, supporting both streams and futures. One of the only ones of its kind.\n\n\nWhen working with Future results, simply pass the \nFuture\n in your template context.\nStreams that carry an encodable type need to be encoded before they're usable within Leaf.\n\n\nstruct\n \nProfile\n:\n \nCodable\n \n{\n\n \nvar\n \nfriends\n:\n \nEncodableStream\n\n \nvar\n \ncurrentUser\n:\n \nFuture\nUser\n\n\n}\n\n\n\n\n\n\nIn the above context, the \ncurrentUser\n variable in Leaf will behave as being a \nUser\n type. Leaf will not read the user Future if it's not used during rendering.\n\n\nEncodableStream\n will behave as an array of LeafData, only with lower memory impact and better performance. It is recommended to use \nEncodableStream\n for (large) database queries.\n\n\nYour name is #(currentUser.name).\n\n#for(friend in friends) {\n #(friend.name) is a friend of you.\n}\n\n\n\n\n\nTemplate syntax\n\n\nStructure\n\n\nLeaf tags are made up of four elements:\n\n\n\n\nToken: \n#\n is the token\n\n\nName: A \nstring\n that identifies the tag\n\n\nParameter List: \n()\n May accept 0 or more arguments\n\n\nBody (optional): \n{}\n Must be separated from the parameter list by a space\n\n\n\n\nThere can be many different usages of these four 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#embed(\"template\")\n\n\n#set(\"title\") { Welcome to Vapor }\n\n\n#count(friends)\n\n\n#for(friend in friends) { \nli\n#(friend.name)\n/li\n }\n\n\n\n\nWorking with context\n\n\nIn our Swift example from earlier, we used an empty \n[String: String]\n dictionary for context, which passes no custom data to Leaf. To try rendering content, use this code instead:\n\n\nlet\n \ncontext\n \n=\n \n[\ntitle\n:\n \nWelcome\n,\n \nmessage\n:\n \nVapor and Leaf work hand in hand\n]\n\n\nreturn\n \ntry\n \nleaf\n.\nmake\n(\nhome\n,\n \ncontext\n)\n\n\n\n\n\n\nThat will expose \ntitle\n and \nmessage\n to our Leaf template, which can then be used inside tags. For example:\n\n\nh1\n#(title)\n/h1\n\n\np\n#(message)\n/p\n\n\n\n\n\n\nChecking conditions\n\n\nLeaf is able to evaluate a range of conditions using its \n#if\n tag. For example, if you provide a variable it will check that variable exists in its context:\n\n\n#if(title) {\n The title is #(title)\n} else {\n No title was provided.\n}\n\n\n\n\n\nYou can also write comparisons, for example:\n\n\n#if(title == \nWelcome\n) {\n This is a friendly web page.\n} else {\n No strangers allowed!\n}\n\n\n\n\n\nIf you want to use another tag as part of your condition, you should omit the \n#\n for the inner tag. For example:\n\n\n#if(lowercase(title) == \nwelcome\n) {\n This is a friendly web page.\n} else {\n No strangers allowed!\n}\n\n\n\n\n\nLoops\n\n\nIf you provide an array of items, Leaf can loop over them and let you manipulate each item individually using its \n#for\n tag. For example, we could update our Swift code to provide a list of names in a team:\n\n\nlet\n \ncontext\n \n=\n \n[\nteam\n:\n \n[\nMalcolm\n,\n \nKaylee\n,\n \nJayne\n]]\n\n\n\n\n\n\nWe could then loop over them in Leaf like this:\n\n\n#for(name in team) {\n \np\n#(name) is in the team.\n/p\n\n}\n\n\n\n\n\nLeaf provides some extra variables inside a \n#for\n loop to give you more information about the loop's progress:\n\n\n\n\nThe \nloop.isFirst\n variable is true when the current iteration is the first one.\n\n\nThe \nloop.isLast\n variable is true when it's the last iteration.\n\n\nThe \nloop.index\n variable will be set to the number of the current iteration, counting from 0.\n\n\n\n\nHere's how we could use a loop variable to print just the first name in our array:\n\n\n#for(name in team) {\n #if(isFirst) { \np\n#(name) is first!\n/p\n }\n}\n\n\n\n\n\nEmbedding templates\n\n\nLeaf\u2019s \n#embed\n tag allows you to copy the contents of one template into another. When use this, you should always omit the template file's .leaf extension.\n\n\nEmbedding is useful for copying in a standard piece of content, for example a page footer or advert code:\n\n\n#embed(\nfooter\n)\n\n\n\n\n\nThis tag is also useful for building one template on top of another. For example, you might have a master.leaf file that includes all the code required to lay out your website \u2013\u00a0HTML structure, CSS and JavaScript \u2013\u00a0with some gaps in place that represent where page content varies.\n\n\nUsing this approach, you would construct a child template that fills in its unique content, then embeds the parent template that places the content appropriately.\n\n\nFor example, you might create a child.leaf template like this:\n\n\n#set(\nbody\n) {\n\np\nWelcome to Vapor!\n/p\n\n}\n\n#embed(\nmaster\n)\n\n\n\n\n\nThat configures one item of context, \nbody\n, but doesn\u2019t display it directly. Instead, it embeds master.leaf, which can render \nbody\n along with any other context variables passed in from Swift. For example, master.leaf might look like this:\n\n\nhtml\n\n\nhead\ntitle\n#(title)\n/title\n/head\n\n\nbody\n#get(body)\n/body\n\n\n/html\n\n\n\n\n\n\nWhen given the context \n[\"title\": \"Hi there!\"]\n, child.leaf will render as follows:\n\n\nhtml\n\n\nhead\ntitle\nHi there!\n/title\n/head\n\n\nbody\np\nWelcome to Vapor!\n/p\n/body\n\n\n/html\n\n\n\n\n\n\nOther tags\n\n\n#capitalize\n\n\nThe \n#capitalize\n tag uppercases the first letter of any string. For example, \u201ctaylor\u201d will become \u201cTaylor\u201d.\n\n\n#capitalize(name)\n\n\n\n\n\n#contains\n\n\nThe \n#contains\n tag accepts an array and a value as its two parameters, and returns true if the array in parameter one contains the value in parameter two. For example, given the array \nteam\n:\n\n\n#if(contains(team, \nJayne\n)) {\n You\nre all set!\n} else {\n You need someone to do PR.\n}\n\n\n\n\n\n#count\n\n\nThe \n#count\n tag returns the number of items in an array. For example:\n\n\nYour search matched #count(matches) pages.\n\n\n\n\n\n#lowercase\n\n\nThe \n#lowercase\n tag lowercases all letters in a string. For example, \u201cTaylor\u201d will become \u201ctaylor\u201d.\n\n\n#lowercase(name)\n\n\n\n\n\n#uppercase\n\n\nThe \n#uppercase\n tag uppercases all letters in a string. For example, \u201cTaylor\u201d will become \u201cTAYLOR\u201d.\n\n\n#uppercase(name)",
|
|
"title": "Basics"
|
|
},
|
|
{
|
|
"location": "/leaf/basics/#basics",
|
|
"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 \u2013 maybe that's Leaf! The goals of Leaf are: Small set of strictly enforced rules Consistency Parser first mentality Extensibility Asynchronous and reactive",
|
|
"title": "Basics"
|
|
},
|
|
{
|
|
"location": "/leaf/basics/#rendering-a-template",
|
|
"text": "Once you have Leaf installed, you should create a directory called \u201cResources\u201d inside your project folder, and inside that create another directory called \u201cViews\u201d. This Resources/Views directory is the default location for Leaf templates, although you can change it if you want. Firstly, import Leaf to routes.swift import Leaf Then, to render a basic Leaf template from a route, add this code: router . get { req - Future View in \n let leaf = try req . make ( LeafRenderer . self ) \n let context = [ String : String ]() \n return try leaf . render ( home , context ) } That will load home.leaf in the Resources/Views directory and render it. The context dictionary is there to let you provide custom data to render inside the template, but you might find it easier to use codable structs instead because they provide extra type safety. For example: struct HomePage : Codable { \n var title : String \n var content : String }",
|
|
"title": "Rendering a template"
|
|
},
|
|
{
|
|
"location": "/leaf/basics/#async",
|
|
"text": "Leaf's engine is completely reactive, supporting both streams and futures. One of the only ones of its kind. When working with Future results, simply pass the Future in your template context.\nStreams that carry an encodable type need to be encoded before they're usable within Leaf. struct Profile : Codable { \n var friends : EncodableStream \n var currentUser : Future User } In the above context, the currentUser variable in Leaf will behave as being a User type. Leaf will not read the user Future if it's not used during rendering. EncodableStream will behave as an array of LeafData, only with lower memory impact and better performance. It is recommended to use EncodableStream for (large) database queries. Your name is #(currentUser.name).\n\n#for(friend in friends) {\n #(friend.name) is a friend of you.\n}",
|
|
"title": "Async"
|
|
},
|
|
{
|
|
"location": "/leaf/basics/#template-syntax",
|
|
"text": "",
|
|
"title": "Template syntax"
|
|
},
|
|
{
|
|
"location": "/leaf/basics/#structure",
|
|
"text": "Leaf tags are made up of four 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 four 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) #embed(\"template\") #set(\"title\") { Welcome to Vapor } #count(friends) #for(friend in friends) { li #(friend.name) /li }",
|
|
"title": "Structure"
|
|
},
|
|
{
|
|
"location": "/leaf/basics/#working-with-context",
|
|
"text": "In our Swift example from earlier, we used an empty [String: String] dictionary for context, which passes no custom data to Leaf. To try rendering content, use this code instead: let context = [ title : Welcome , message : Vapor and Leaf work hand in hand ] return try leaf . make ( home , context ) That will expose title and message to our Leaf template, which can then be used inside tags. For example: h1 #(title) /h1 p #(message) /p",
|
|
"title": "Working with context"
|
|
},
|
|
{
|
|
"location": "/leaf/basics/#checking-conditions",
|
|
"text": "Leaf is able to evaluate a range of conditions using its #if tag. For example, if you provide a variable it will check that variable exists in its context: #if(title) {\n The title is #(title)\n} else {\n No title was provided.\n} You can also write comparisons, for example: #if(title == Welcome ) {\n This is a friendly web page.\n} else {\n No strangers allowed!\n} If you want to use another tag as part of your condition, you should omit the # for the inner tag. For example: #if(lowercase(title) == welcome ) {\n This is a friendly web page.\n} else {\n No strangers allowed!\n}",
|
|
"title": "Checking conditions"
|
|
},
|
|
{
|
|
"location": "/leaf/basics/#loops",
|
|
"text": "If you provide an array of items, Leaf can loop over them and let you manipulate each item individually using its #for tag. For example, we could update our Swift code to provide a list of names in a team: let context = [ team : [ Malcolm , Kaylee , Jayne ]] We could then loop over them in Leaf like this: #for(name in team) {\n p #(name) is in the team. /p \n} Leaf provides some extra variables inside a #for loop to give you more information about the loop's progress: The loop.isFirst variable is true when the current iteration is the first one. The loop.isLast variable is true when it's the last iteration. The loop.index variable will be set to the number of the current iteration, counting from 0. Here's how we could use a loop variable to print just the first name in our array: #for(name in team) {\n #if(isFirst) { p #(name) is first! /p }\n}",
|
|
"title": "Loops"
|
|
},
|
|
{
|
|
"location": "/leaf/basics/#embedding-templates",
|
|
"text": "Leaf\u2019s #embed tag allows you to copy the contents of one template into another. When use this, you should always omit the template file's .leaf extension. Embedding is useful for copying in a standard piece of content, for example a page footer or advert code: #embed( footer ) This tag is also useful for building one template on top of another. For example, you might have a master.leaf file that includes all the code required to lay out your website \u2013\u00a0HTML structure, CSS and JavaScript \u2013\u00a0with some gaps in place that represent where page content varies. Using this approach, you would construct a child template that fills in its unique content, then embeds the parent template that places the content appropriately. For example, you might create a child.leaf template like this: #set( body ) { p Welcome to Vapor! /p \n}\n\n#embed( master ) That configures one item of context, body , but doesn\u2019t display it directly. Instead, it embeds master.leaf, which can render body along with any other context variables passed in from Swift. For example, master.leaf might look like this: html head title #(title) /title /head body #get(body) /body /html When given the context [\"title\": \"Hi there!\"] , child.leaf will render as follows: html head title Hi there! /title /head body p Welcome to Vapor! /p /body /html",
|
|
"title": "Embedding templates"
|
|
},
|
|
{
|
|
"location": "/leaf/basics/#other-tags",
|
|
"text": "",
|
|
"title": "Other tags"
|
|
},
|
|
{
|
|
"location": "/leaf/basics/#capitalize",
|
|
"text": "The #capitalize tag uppercases the first letter of any string. For example, \u201ctaylor\u201d will become \u201cTaylor\u201d. #capitalize(name)",
|
|
"title": "#capitalize"
|
|
},
|
|
{
|
|
"location": "/leaf/basics/#contains",
|
|
"text": "The #contains tag accepts an array and a value as its two parameters, and returns true if the array in parameter one contains the value in parameter two. For example, given the array team : #if(contains(team, Jayne )) {\n You re all set!\n} else {\n You need someone to do PR.\n}",
|
|
"title": "#contains"
|
|
},
|
|
{
|
|
"location": "/leaf/basics/#count",
|
|
"text": "The #count tag returns the number of items in an array. For example: Your search matched #count(matches) pages.",
|
|
"title": "#count"
|
|
},
|
|
{
|
|
"location": "/leaf/basics/#lowercase",
|
|
"text": "The #lowercase tag lowercases all letters in a string. For example, \u201cTaylor\u201d will become \u201ctaylor\u201d. #lowercase(name)",
|
|
"title": "#lowercase"
|
|
},
|
|
{
|
|
"location": "/leaf/basics/#uppercase",
|
|
"text": "The #uppercase tag uppercases all letters in a string. For example, \u201cTaylor\u201d will become \u201cTAYLOR\u201d. #uppercase(name)",
|
|
"title": "#uppercase"
|
|
},
|
|
{
|
|
"location": "/leaf/custom-tags/",
|
|
"text": "Custom Tags\n\n\nYou can extend Leaf to provide your own tags that add custom functionality. To demonstrate this, let's look at a basic example by recreating \n#uppercase\n together. This tag will take one argument, which is the string to uppercase.\n\n\nWhen working with custom tags, there are four important things to know:\n\n\n\n\nYou should call \nrequireParameterCount()\n with the number of parameters you expect to receive. This will throw an error if your tag is used incorrectly.\n\n\nIf you do or do not require a body, you should use either \nrequireBody()\n or \nrequireNoBody()\n. Again, this will throw an error if your tag is used incorrectly.\n\n\nYou can read individual parameters using the \nparameters\n array. Each parameter will be of type \nLeafData\n, which you can convert to concrete data types using properties such as \n.string\n, \n.dictionary\n, and so on.\n\n\nYou must return a \nFuture\nLeafData?\n containing what should be rendered. In the example below we wrap the resulting uppercase string in a \nLeafData\n string, then send that back wrapped in a future.\n\n\n\n\nHere\u2019s example code for a \nCustomUppercase\n Leaf tag:\n\n\nimport\n \nAsync\n\n\nimport\n \nLeaf\n\n\n\npublic\n \nfinal\n \nclass\n \nCustomUppercase\n:\n \nLeaf\n.\nLeafTag\n \n{\n\n \npublic\n \ninit\n()\n \n{}\n\n \npublic\n \nfunc\n \nrender\n(\nparsed\n:\n \nParsedTag\n,\n \ncontext\n:\n \nLeafContext\n,\n \nrenderer\n:\n \nLeafRenderer\n)\n \nthrows\n \n-\n \nFuture\nLeafData\n?\n \n{\n\n \n// ensure we receive precisely one parameter\n\n \ntry\n \nparsed\n.\nrequireParameterCount\n(\n1\n)\n\n\n \n// pull out our lone parameter as a string then uppercase it, or use an empty string\n\n \nlet\n \nstring\n \n=\n \nparsed\n.\nparameters\n[\n0\n].\nstring\n?.\nuppercased\n()\n \n??\n \n\n\n \n// send it back wrapped in a LeafData\n\n \nreturn\n \nFuture\n(.\nstring\n(\nstring\n))\n\n \n}\n\n\n}\n\n\n\n\n\n\nWe can now register this Tag in our \nconfigure.swift\n file with:\n\n\nservices\n.\nregister\n \n{\n \ncontainer\n \n-\n \nLeafConfig\n \nin\n\n \n// take a copy of Leaf\ns default tags\n\n \nvar\n \ntags\n \n=\n \ndefaultTags\n\n\n \n// add our custom tag\n\n \ntags\n[\ncustomuppercase\n]\n \n=\n \nCustomUppercase\n()\n\n\n \n// find the location of our Resources/Views directory\n\n \nlet\n \ndirectoryConfig\n \n=\n \ntry\n \ncontainer\n.\nmake\n(\nDirectoryConfig\n.\nself\n,\n \nfor\n:\n \nLeafRenderer\n.\nself\n)\n\n \nlet\n \nviewsDirectory\n \n=\n \ndirectoryConfig\n.\nworkDir\n \n+\n \nResources/Views\n\n\n \n// put all that into a new Leaf configuration and return it\n\n \nreturn\n \nLeafConfig\n(\ntags\n:\n \ntags\n,\n \nviewsDir\n:\n \nviewsDirectory\n)\n\n\n}\n\n\n\n\n\n\nOnce that is complete, you can use \n#customuppercase(some_variable)\n to run your custom code.\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.",
|
|
"title": "Custom tags"
|
|
},
|
|
{
|
|
"location": "/leaf/custom-tags/#custom-tags",
|
|
"text": "You can extend Leaf to provide your own tags that add custom functionality. To demonstrate this, let's look at a basic example by recreating #uppercase together. This tag will take one argument, which is the string to uppercase. When working with custom tags, there are four important things to know: You should call requireParameterCount() with the number of parameters you expect to receive. This will throw an error if your tag is used incorrectly. If you do or do not require a body, you should use either requireBody() or requireNoBody() . Again, this will throw an error if your tag is used incorrectly. You can read individual parameters using the parameters array. Each parameter will be of type LeafData , which you can convert to concrete data types using properties such as .string , .dictionary , and so on. You must return a Future LeafData? containing what should be rendered. In the example below we wrap the resulting uppercase string in a LeafData string, then send that back wrapped in a future. Here\u2019s example code for a CustomUppercase Leaf tag: import Async import Leaf public final class CustomUppercase : Leaf . LeafTag { \n public init () {} \n public func render ( parsed : ParsedTag , context : LeafContext , renderer : LeafRenderer ) throws - Future LeafData ? { \n // ensure we receive precisely one parameter \n try parsed . requireParameterCount ( 1 ) \n\n // pull out our lone parameter as a string then uppercase it, or use an empty string \n let string = parsed . parameters [ 0 ]. string ?. uppercased () ?? \n\n // send it back wrapped in a LeafData \n return Future (. string ( string )) \n } } We can now register this Tag in our configure.swift file with: services . register { container - LeafConfig in \n // take a copy of Leaf s default tags \n var tags = defaultTags \n\n // add our custom tag \n tags [ customuppercase ] = CustomUppercase () \n\n // find the location of our Resources/Views directory \n let directoryConfig = try container . make ( DirectoryConfig . self , for : LeafRenderer . self ) \n let viewsDirectory = directoryConfig . workDir + Resources/Views \n\n // put all that into a new Leaf configuration and return it \n return LeafConfig ( tags : tags , viewsDir : viewsDirectory ) } Once that is complete, you can use #customuppercase(some_variable) to run your custom code. 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": "/logging/getting-started/",
|
|
"text": "Getting Started with Logging\n\n\nThe Logging module is provided as a part of Vapor's Console package (\nvapor/console\n). This module provides convenience APIs for creating log \n\n\n\n\nTip\n\n\nFor an in-depth look at all of Logging's APIs, check out the \nLogging API docs\n.\n\n\n\n\nUsage\n\n\nThis package is included with Vapor and exported by default. You will have access to all \nLogging\n APIs when you import \nVapor\n.\n\n\nimport\n \nVapor\n \n// implies import Logging\n\n\n\n\n\n\nStandalone\n\n\nThe Logging module, part of the larger Vapor Console package, can also be used on its own with any Swift project.\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\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/// \ud83d\udcbb APIs for creating interactive CLI tools.\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/console.git\n,\n \nfrom\n:\n \n3.0.0\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nLogging\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Logging\n to access the APIs.\n\n\nOverview\n\n\nContinue to \nLogging \u2192 Overview\n for an overview of Logging's features.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/logging/getting-started/#getting-started-with-logging",
|
|
"text": "The Logging module is provided as a part of Vapor's Console package ( vapor/console ). This module provides convenience APIs for creating log Tip For an in-depth look at all of Logging's APIs, check out the Logging API docs .",
|
|
"title": "Getting Started with Logging"
|
|
},
|
|
{
|
|
"location": "/logging/getting-started/#usage",
|
|
"text": "This package is included with Vapor and exported by default. You will have access to all Logging APIs when you import Vapor . import Vapor // implies import Logging",
|
|
"title": "Usage"
|
|
},
|
|
{
|
|
"location": "/logging/getting-started/#standalone",
|
|
"text": "The Logging module, part of the larger Vapor Console package, can also be used on its own with any Swift project. To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n /// \ud83d\udcbb APIs for creating interactive CLI tools. \n . package ( url : https://github.com/vapor/console.git , from : 3.0.0 ), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ Logging , ... ]) \n ] ) Use import Logging to access the APIs.",
|
|
"title": "Standalone"
|
|
},
|
|
{
|
|
"location": "/logging/getting-started/#overview",
|
|
"text": "Continue to Logging \u2192 Overview for an overview of Logging's features.",
|
|
"title": "Overview"
|
|
},
|
|
{
|
|
"location": "/logging/overview/",
|
|
"text": "Logging Overview\n\n\nThe logging package provides convenience APIs for logging information while your app is running. The \nLogger\n protocol declares a common interface for logging information. A default \nPrintLogger\n is available, but you can implement custom loggers to suit your specific needs.\n\n\nLog\n\n\nFirst, you will want to use a \nContainer\n to create an instance of \nLogger\n. Then you can use the convenience methods to log information.\n\n\nlet\n \nlogger\n \n=\n \ntry\n \nreq\n.\nmake\n(\nLogger\n.\nself\n)\n\n\nlogger\n.\ninfo\n(\nLogger created!\n)\n\n\n\n\n\n\nSee \nLogger\n in the API docs for a list of all available methods.\n\n\nCheck out \nService \n Services\n for more information on how to register a custom logger.",
|
|
"title": "Overview"
|
|
},
|
|
{
|
|
"location": "/logging/overview/#logging-overview",
|
|
"text": "The logging package provides convenience APIs for logging information while your app is running. The Logger protocol declares a common interface for logging information. A default PrintLogger is available, but you can implement custom loggers to suit your specific needs.",
|
|
"title": "Logging Overview"
|
|
},
|
|
{
|
|
"location": "/logging/overview/#log",
|
|
"text": "First, you will want to use a Container to create an instance of Logger . Then you can use the convenience methods to log information. let logger = try req . make ( Logger . self ) logger . info ( Logger created! ) See Logger in the API docs for a list of all available methods. Check out Service Services for more information on how to register a custom logger.",
|
|
"title": "Log"
|
|
},
|
|
{
|
|
"location": "/multipart/getting-started/",
|
|
"text": "Getting Started with Multipart\n\n\nMultipart (\nvapor/multipart\n) is a small package that helps you parse and serialize \nmultipart\n encoded data. Multipart is a widely-supported encoding on the web. It's most often used for serializing web forms, especially ones that contain rich media like images.\n\n\nThe Multipart package makes it easy to use this encoding by integrating directly with \nCodable\n.\n\n\nVapor\n\n\nThis package is included with Vapor and exported by default. You will have access to all \nMultipart\n APIs when you import \nVapor\n.\n\n\nimport\n \nVapor\n\n\n\n\n\n\nStandalone\n\n\nThe Multipart package is lightweight, pure-Swift, and has very few dependencies. This means it can be used to work with multipart-encoded data for any Swift project\neven one not using Vapor.\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\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/multipart.git\n,\n \nfrom\n:\n \n3.0.0\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nMultipart\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Multipart\n to access the APIs.\n\n\n\n\nWarning\n\n\nSome of this guide may contain Vapor-specific APIs, however most of it should be applicable to the Multipart package in general.\nVisit the \nAPI Docs\n for Multipart-specific API info.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/multipart/getting-started/#getting-started-with-multipart",
|
|
"text": "Multipart ( vapor/multipart ) is a small package that helps you parse and serialize multipart encoded data. Multipart is a widely-supported encoding on the web. It's most often used for serializing web forms, especially ones that contain rich media like images. The Multipart package makes it easy to use this encoding by integrating directly with Codable .",
|
|
"title": "Getting Started with Multipart"
|
|
},
|
|
{
|
|
"location": "/multipart/getting-started/#vapor",
|
|
"text": "This package is included with Vapor and exported by default. You will have access to all Multipart APIs when you import Vapor . import Vapor",
|
|
"title": "Vapor"
|
|
},
|
|
{
|
|
"location": "/multipart/getting-started/#standalone",
|
|
"text": "The Multipart package is lightweight, pure-Swift, and has very few dependencies. This means it can be used to work with multipart-encoded data for any Swift project even one not using Vapor. To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . package ( url : https://github.com/vapor/multipart.git , from : 3.0.0 ), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ Multipart , ... ]) \n ] ) Use import Multipart to access the APIs. Warning Some of this guide may contain Vapor-specific APIs, however most of it should be applicable to the Multipart package in general.\nVisit the API Docs for Multipart-specific API info.",
|
|
"title": "Standalone"
|
|
},
|
|
{
|
|
"location": "/multipart/overview/",
|
|
"text": "Using Multipart\n\n\nMultipart is a widely-supported encoding on the web. It's most often used for serializing web forms, especially ones that contain rich media like images. It allows for arbitrary data to be encoded in each part thanks to a unique delimiter \nboundary\n that is defined separately. This boundary is guaranteed by the client to not appear anywhere in the data.\n\n\nMultipart is a powerful encoding, however it is rarely used in its base format. Most commonly, \nmultipart/form-data\n is used. This encoding adds a \n\"name\"\n property to each part of the multipart data. This is required for serializing web forms. For the rest of this guide, assume we are talking about \nmultipart/form-data\n unless otherwise specified.\n\n\n\n\nTip\n\n\nMultipart integrates with \nContent\n like all other encoding methods in Vapor. See \nVapor \n Content\n for more information about the \nContent\n protocol. \n\n\n\n\nLet's take a look at how to decode a \nmultipart/form-data\n-encoded request.\n\n\nDecode\n\n\nMost often, you will be decoding \nmultipart/form-data\n-encoded requests from a web form. Let's take a look at what one of these requests might look like. After that, we will take a look at what the HTML form for that request would look like.\n\n\nRequest\n\n\nHere is an example \nmultipart/form-data\n-encoded request for creating a new user.\n\n\nPOST\n \n/users\n \nHTTP\n/\n1.1\n\n\nContent-Type\n:\n \nmultipart/form-data; boundary=123\n\n\n--123\nContent-Disposition: form-data; name=\nname\n\n\nVapor\n--123\nContent-Disposition: form-data; name=\nage\n\n\n3\n--123\nContent-Disposition: form-data; name=\nimage\n; filename=\ndroplet.png\n\n\n\ncontents of image\n\n--123--\n\n\n\n\n\nYou can see the multipart data uses a \nboundary\n (in this case it is \n\"123\"\n) to separate the data. This will usually be a longer string. The client sending a multipart-encoded request must ensure that the boundary it supplies does not appear anywhere in the content it is sending you. That's what allows this encoding to be used to send things like files.\n\n\nForm\n\n\nThere are many ways to create a multipart-encoded request, but the most common is an HTML web form. Here is what the HTML form for this request might have looked like.\n\n\nform\n \nmethod\n=\nPOST\n \naction\n=\n/users\n \nenctype\n=\nmultipart/form-data\n\n \ninput\n \ntype\n=\ntext\n \nname\n=\nname\n\n \ninput\n \ntype\n=\ntext\n \nname\n=\nage\n\n \ninput\n \ntype\n=\nfile\n \nname\n=\nimage\n\n\n/\nform\n\n\n\n\n\n\nTake note of the \nenctype\n attribute on the \nform\n as well as the \nfile\n type input. This is what allows us to send files via the web form.\n\n\nContent\n\n\nNow let's take a look at how we would handle this request in Vapor. The first step (as always with \nContent\n) is to create a \nCodable\n struct that represents the data structure.\n\n\nimport\n \nVapor\n\n\n\nstruct\n \nUser\n:\n \nContent\n \n{\n\n \nvar\n \nname\n:\n \nString\n\n \nvar\n \nage\n:\n \nInt\n\n \nvar\n \nimage\n:\n \nData\n\n\n}\n\n\n\n\n\n\n\n\nTip\n\n\nYou can use \nFile\n instead of \nData\n if you would also like to access the filename.\n\n\n\n\nNow that we have our \nUser\n struct, let's decode that request! We can use the \nContentContainer\n to do this easily.\n\n\nrouter\n.\npost\n(\nusers\n)\n \n{\n \nreq\n \n-\n \nFuture\nHTTPStatus\n \nin\n\n \nreturn\n \ntry\n \nreq\n.\ncontent\n.\ndecode\n(\nUser\n.\nself\n).\nmap\n(\nto\n:\n \nHTTPStatus\n.\nself\n)\n \n{\n \nuser\n \nin\n\n \nprint\n(\nuser\n.\nname\n)\n \n// \nVapor\n\n \nprint\n(\nuser\n.\nage\n)\n \n// 3\n\n \nprint\n(\nuser\n.\nimage\n)\n \n// Raw image data\n\n \nreturn\n \n.\nok\n\n \n}\n\n\n}\n\n\n\n\n\n\nNow when you post the form to \n/users\n, you should see the information printed in the console. Nice work!\n\n\nEncode\n\n\nAPIs encode multipart data much less often than they decode it. However, encoding is just as easy with Vapor. Using our same \nUser\n struct from the previous example, here is how we can encode a multipart-encoded response.\n\n\nrouter\n.\nget\n(\nmultipart\n)\n \n{\n \nreq\n \n-\n \nUser\n \nin\n\n \nlet\n \nres\n \n=\n \nreq\n.\nmakeResponse\n()\n\n \nlet\n \nuser\n \n=\n \nUser\n(\nname\n:\n \nVapor\n,\n \nage\n:\n \n3\n,\n \nimage\n:\n \nData\n(...))\n\n \nres\n.\ncontent\n.\nencode\n(\nuser\n,\n \nas\n:\n \n.\nformData\n)\n\n \nreturn\n \nuser\n\n\n}\n\n\n\n\n\n\n\n\nTip\n\n\nIf you set a default \nMediaType\n on your \nContent\n types, then you can return them directly in the route closure.\n\n\n\n\nParsing \n Serializing\n\n\nThe Multipart package also offers APIs for parsing and serializing \nmultipart/form-data\n data without using \nCodable\n. Check out the \nAPI Docs\n for more information on using those APIs.",
|
|
"title": "Overview"
|
|
},
|
|
{
|
|
"location": "/multipart/overview/#using-multipart",
|
|
"text": "Multipart is a widely-supported encoding on the web. It's most often used for serializing web forms, especially ones that contain rich media like images. It allows for arbitrary data to be encoded in each part thanks to a unique delimiter boundary that is defined separately. This boundary is guaranteed by the client to not appear anywhere in the data. Multipart is a powerful encoding, however it is rarely used in its base format. Most commonly, multipart/form-data is used. This encoding adds a \"name\" property to each part of the multipart data. This is required for serializing web forms. For the rest of this guide, assume we are talking about multipart/form-data unless otherwise specified. Tip Multipart integrates with Content like all other encoding methods in Vapor. See Vapor Content for more information about the Content protocol. Let's take a look at how to decode a multipart/form-data -encoded request.",
|
|
"title": "Using Multipart"
|
|
},
|
|
{
|
|
"location": "/multipart/overview/#decode",
|
|
"text": "Most often, you will be decoding multipart/form-data -encoded requests from a web form. Let's take a look at what one of these requests might look like. After that, we will take a look at what the HTML form for that request would look like.",
|
|
"title": "Decode"
|
|
},
|
|
{
|
|
"location": "/multipart/overview/#request",
|
|
"text": "Here is an example multipart/form-data -encoded request for creating a new user. POST /users HTTP / 1.1 Content-Type : multipart/form-data; boundary=123 \n\n--123\nContent-Disposition: form-data; name= name \n\nVapor\n--123\nContent-Disposition: form-data; name= age \n\n3\n--123\nContent-Disposition: form-data; name= image ; filename= droplet.png contents of image \n--123-- You can see the multipart data uses a boundary (in this case it is \"123\" ) to separate the data. This will usually be a longer string. The client sending a multipart-encoded request must ensure that the boundary it supplies does not appear anywhere in the content it is sending you. That's what allows this encoding to be used to send things like files.",
|
|
"title": "Request"
|
|
},
|
|
{
|
|
"location": "/multipart/overview/#form",
|
|
"text": "There are many ways to create a multipart-encoded request, but the most common is an HTML web form. Here is what the HTML form for this request might have looked like. form method = POST action = /users enctype = multipart/form-data \n input type = text name = name \n input type = text name = age \n input type = file name = image / form Take note of the enctype attribute on the form as well as the file type input. This is what allows us to send files via the web form.",
|
|
"title": "Form"
|
|
},
|
|
{
|
|
"location": "/multipart/overview/#content",
|
|
"text": "Now let's take a look at how we would handle this request in Vapor. The first step (as always with Content ) is to create a Codable struct that represents the data structure. import Vapor struct User : Content { \n var name : String \n var age : Int \n var image : Data } Tip You can use File instead of Data if you would also like to access the filename. Now that we have our User struct, let's decode that request! We can use the ContentContainer to do this easily. router . post ( users ) { req - Future HTTPStatus in \n return try req . content . decode ( User . self ). map ( to : HTTPStatus . self ) { user in \n print ( user . name ) // Vapor \n print ( user . age ) // 3 \n print ( user . image ) // Raw image data \n return . ok \n } } Now when you post the form to /users , you should see the information printed in the console. Nice work!",
|
|
"title": "Content"
|
|
},
|
|
{
|
|
"location": "/multipart/overview/#encode",
|
|
"text": "APIs encode multipart data much less often than they decode it. However, encoding is just as easy with Vapor. Using our same User struct from the previous example, here is how we can encode a multipart-encoded response. router . get ( multipart ) { req - User in \n let res = req . makeResponse () \n let user = User ( name : Vapor , age : 3 , image : Data (...)) \n res . content . encode ( user , as : . formData ) \n return user } Tip If you set a default MediaType on your Content types, then you can return them directly in the route closure.",
|
|
"title": "Encode"
|
|
},
|
|
{
|
|
"location": "/multipart/overview/#parsing-serializing",
|
|
"text": "The Multipart package also offers APIs for parsing and serializing multipart/form-data data without using Codable . Check out the API Docs for more information on using those APIs.",
|
|
"title": "Parsing & Serializing"
|
|
},
|
|
{
|
|
"location": "/mysql/getting-started/",
|
|
"text": "Warning\n\n\nMySQL 3.0 is still in beta. Some documentation may be missing or out of date.\n\n\n\n\nGetting Started with MySQL\n\n\nMySQL\n is a widely-used, open-source database. It has an extremely popular wire-protocol that other open-source databases like \nMariaDB\n also support.\n\n\nYou can use MySQL with Vapor (or any server-side Swift framework) by either:\n\n\n\n\nUsing \nFluent MySQL\n ORM.\n\n\nUse just \nMySQL core\n.\n\n\n\n\nWe recommend using the ORM since it does a lot of the hard work for you. Check out the respective guides to learn more.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/mysql/getting-started/#getting-started-with-mysql",
|
|
"text": "MySQL is a widely-used, open-source database. It has an extremely popular wire-protocol that other open-source databases like MariaDB also support. You can use MySQL with Vapor (or any server-side Swift framework) by either: Using Fluent MySQL ORM. Use just MySQL core . We recommend using the ORM since it does a lot of the hard work for you. Check out the respective guides to learn more.",
|
|
"title": "Getting Started with MySQL"
|
|
},
|
|
{
|
|
"location": "/mysql/fluent/",
|
|
"text": "Fluent MySQL\n\n\nFluent MySQL (\nvapor/fluent-mysql\n) is a type-safe, fast, and easy-to-use ORM for MySQL built on top of \nFluent\n.\n\n\n\n\nSeealso\n\n\nThe Fluent MySQL package is built on top of \nFluent\n and the pure Swift, NIO-based \nMySQL core\n. You should refer to their guides for more information about subjects not covered here.\n\n\n\n\nGetting Started\n\n\nThis section will show you how to add Fluent MySQL to your project and create your first \nMySQLModel\n.\n\n\nPackage\n\n\nThe first step to using Fluent MySQL is adding it as a dependency to your project in your SPM package manifest file.\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\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/// Any other dependencies ...\n\n\n \n// \ud83d\udd8b\ud83d\udc2c Swift ORM (queries, models, relations, etc) built on MySQL.\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/fluent-mysql.git\n,\n \nfrom\n:\n \n3.0.0-rc\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nApp\n,\n \ndependencies\n:\n \n[\nFluentMySQL\n,\n \n...]),\n\n \n.\ntarget\n(\nname\n:\n \nRun\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n.\ntestTarget\n(\nname\n:\n \nAppTests\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n]\n\n\n)\n\n\n\n\n\n\nDon't forget to add the module as a dependency in the \ntargets\n array. Once you have added the dependency, regenerate your Xcode project with the following command:\n\n\nvapor xcode\n\n\n\n\n\nModel\n\n\nNow let's create our first \nMySQLModel\n. Models represent tables in your MySQL database and they are the primary method of interacting with your data. \n\n\n/// A simple user.\n\n\nfinal\n \nclass\n \nUser\n:\n \nMySQLModel\n \n{\n\n \n/// The unique identifier for this user.\n\n \nvar\n \nid\n:\n \nInt\n?\n\n\n \n/// The user\ns full name.\n\n \nvar\n \nname\n:\n \nString\n\n\n \n/// The user\ns current age in years.\n\n \nvar\n \nage\n:\n \nInt\n\n\n \n/// Creates a new user.\n\n \ninit\n(\nid\n:\n \nInt\n?\n \n=\n \nnil\n,\n \nname\n:\n \nString\n,\n \nage\n:\n \nInt\n)\n \n{\n\n \nself\n.\nid\n \n=\n \nid\n\n \nself\n.\nname\n \n=\n \nname\n\n \nself\n.\nage\n \n=\n \nage\n\n \n}\n\n\n}\n\n\n\n\n\n\nThe example above shows a \nMySQLModel\n for a simple model representing a user. You can make both \nstruct\ns and \nclass\nes a model. You can even conform types that come from external modules. The only requirement is that these types conform to \nCodable\n, which must be declared on the base type for synthesized (automatic) conformance.\n\n\nStandard practice with MySQL databases is using an auto-generated \nINTEGER\n for creating and storing unique identifiers in the \nid\n column. It's also possible to use \nUUID\ns or even \nString\ns for your identifiers. There are convenience protocol for that. \n\n\n\n\n\n\n\n\nprotocol\n\n\ntype\n\n\nkey\n\n\n\n\n\n\n\n\n\n\nMySQLModel\n\n\nInt\n\n\nid\n\n\n\n\n\n\nMySQLUUIDModel\n\n\nUUID\n\n\nid\n\n\n\n\n\n\nMySQLStringModel\n\n\nString\n\n\nid\n\n\n\n\n\n\n\n\n\n\nSeealso\n\n\nTake a look at \nFluent \n Model\n for more information on creating models with custom ID types and keys.\n\n\n\n\nMigration\n\n\nAll of your models (with some rare exceptions) should have a corresponding table\nor \nschema\nin your database. You can use a \nFluent \n Migration\n to automatically generate this schema in a testable, maintainable way. Fluent makes it easy to automatically generate a migration for your model\n\n\n\n\nTip\n\n\nIf you are creating models to represent an existing table or database, you can skip this step.\n\n\n\n\n/// Allows `User` to be used as a migration.\n\n\nextension\n \nUser\n:\n \nMigration\n \n{\n \n}\n\n\n\n\n\n\nThat's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it.\n\n\nTake a look at \nFluent \n Migration\n if you are interested in customizing this migration.\n\n\nConfigure\n\n\nThe final step is to configure your database. At a minimum, this requires adding two things to your \nconfigure.swift\n file.\n\n\n\n\nFluentMySQLProvider\n\n\nMigrationConfig\n\n\n\n\nLet's take a look.\n\n\nimport\n \nFluentMySQL\n\n\n\n/// ...\n\n\n\n/// Register providers first\n\n\ntry\n \nservices\n.\nregister\n(\nFluentMySQLProvider\n())\n\n\n\n/// Configure migrations\n\n\nvar\n \nmigrations\n \n=\n \nMigrationConfig\n()\n\n\nmigrations\n.\nadd\n(\nmodel\n:\n \nUser\n.\nself\n,\n \ndatabase\n:\n \n.\nmysql\n)\n\n\nservices\n.\nregister\n(\nmigrations\n)\n\n\n\n/// Other services....\n\n\n\n\n\n\nRegistering the provider will add all of the services required for Fluent MySQL to work properly. It also includes a default database config struct that uses typical development environment credentials. \n\n\nYou can of course override this config struct if you have non-standard credentials.\n\n\n/// Register custom MySQL Config\n\n\nlet\n \nmysqlConfig\n \n=\n \nMySQLDatabaseConfig\n(\nhostname\n:\n \nlocalhost\n,\n \nport\n:\n \n3306\n,\n \nusername\n:\n \nvapor\n)\n\n\nservices\n.\nregister\n(\nmysqlConfig\n)\n\n\n\n\n\n\nOnce you have the \nMigrationConfig\n added, you should be able to run your application and see the following:\n\n\nMigrating mysql DB\nMigrations \ncomplete\n\nServer starting on http://localhost:8080\n\n\n\n\n\nQuery\n\n\nNow that you have created a model and a corresponding schema in your database, let's make your first query.\n\n\nrouter\n.\nget\n(\nusers\n)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nUser\n.\nquery\n(\non\n:\n \nreq\n).\nall\n()\n\n\n}\n\n\n\n\n\n\nIf you run your app, and query that route, you should see an empty array returned. Now you just need to add some users! Congratulations on getting your first Fluent MySQL model and migration working.\n\n\nConnection\n\n\nWith Fluent, you always have access to the underlying database driver. Using this underlying driver to perform a query is sometimes called a \"raw query\".\n\n\nLet's take a look at a raw MySQL query.\n\n\nrouter\n.\nget\n(\nmysql-version\n)\n \n{\n \nreq\n \n-\n \nFuture\nString\n \nin\n\n \nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nmysql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nselect @@version as v;\n).\nmap\n(\nto\n:\n \nString\n.\nself\n)\n \n{\n \nrows\n \nin\n\n \nreturn\n \ntry\n \nrows\n[\n0\n].\nfirstValue\n(\nforColumn\n:\n \nv\n)?.\ndecode\n(\nString\n.\nself\n)\n \n??\n \nn/a\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nIn the above example, \nwithPooledConnection(to:)\n is used to create a connection to the database identified by \n.mysql\n. This is the default database identifier. See \nFluent \n Database\n to learn more.\n\n\nOnce we have the \nMySQLConnection\n, we can perform a query on it. You can learn more about the methods available in \nMySQL \n Core\n.",
|
|
"title": "Fluent MySQL"
|
|
},
|
|
{
|
|
"location": "/mysql/fluent/#fluent-mysql",
|
|
"text": "Fluent MySQL ( vapor/fluent-mysql ) is a type-safe, fast, and easy-to-use ORM for MySQL built on top of Fluent . Seealso The Fluent MySQL package is built on top of Fluent and the pure Swift, NIO-based MySQL core . You should refer to their guides for more information about subjects not covered here.",
|
|
"title": "Fluent MySQL"
|
|
},
|
|
{
|
|
"location": "/mysql/fluent/#getting-started",
|
|
"text": "This section will show you how to add Fluent MySQL to your project and create your first MySQLModel .",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/mysql/fluent/#package",
|
|
"text": "The first step to using Fluent MySQL is adding it as a dependency to your project in your SPM package manifest file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : MyApp , \n dependencies : [ \n /// Any other dependencies ... \n\n // \ud83d\udd8b\ud83d\udc2c Swift ORM (queries, models, relations, etc) built on MySQL. \n . package ( url : https://github.com/vapor/fluent-mysql.git , from : 3.0.0-rc ), \n ], \n targets : [ \n . target ( name : App , dependencies : [ FluentMySQL , ...]), \n . target ( name : Run , dependencies : [ App ]), \n . testTarget ( name : AppTests , dependencies : [ App ]), \n ] ) Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command: vapor xcode",
|
|
"title": "Package"
|
|
},
|
|
{
|
|
"location": "/mysql/fluent/#model",
|
|
"text": "Now let's create our first MySQLModel . Models represent tables in your MySQL database and they are the primary method of interacting with your data. /// A simple user. final class User : MySQLModel { \n /// The unique identifier for this user. \n var id : Int ? \n\n /// The user s full name. \n var name : String \n\n /// The user s current age in years. \n var age : Int \n\n /// Creates a new user. \n init ( id : Int ? = nil , name : String , age : Int ) { \n self . id = id \n self . name = name \n self . age = age \n } } The example above shows a MySQLModel for a simple model representing a user. You can make both struct s and class es a model. You can even conform types that come from external modules. The only requirement is that these types conform to Codable , which must be declared on the base type for synthesized (automatic) conformance. Standard practice with MySQL databases is using an auto-generated INTEGER for creating and storing unique identifiers in the id column. It's also possible to use UUID s or even String s for your identifiers. There are convenience protocol for that. protocol type key MySQLModel Int id MySQLUUIDModel UUID id MySQLStringModel String id Seealso Take a look at Fluent Model for more information on creating models with custom ID types and keys.",
|
|
"title": "Model"
|
|
},
|
|
{
|
|
"location": "/mysql/fluent/#migration",
|
|
"text": "All of your models (with some rare exceptions) should have a corresponding table or schema in your database. You can use a Fluent Migration to automatically generate this schema in a testable, maintainable way. Fluent makes it easy to automatically generate a migration for your model Tip If you are creating models to represent an existing table or database, you can skip this step. /// Allows `User` to be used as a migration. extension User : Migration { } That's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it. Take a look at Fluent Migration if you are interested in customizing this migration.",
|
|
"title": "Migration"
|
|
},
|
|
{
|
|
"location": "/mysql/fluent/#configure",
|
|
"text": "The final step is to configure your database. At a minimum, this requires adding two things to your configure.swift file. FluentMySQLProvider MigrationConfig Let's take a look. import FluentMySQL /// ... /// Register providers first try services . register ( FluentMySQLProvider ()) /// Configure migrations var migrations = MigrationConfig () migrations . add ( model : User . self , database : . mysql ) services . register ( migrations ) /// Other services.... Registering the provider will add all of the services required for Fluent MySQL to work properly. It also includes a default database config struct that uses typical development environment credentials. You can of course override this config struct if you have non-standard credentials. /// Register custom MySQL Config let mysqlConfig = MySQLDatabaseConfig ( hostname : localhost , port : 3306 , username : vapor ) services . register ( mysqlConfig ) Once you have the MigrationConfig added, you should be able to run your application and see the following: Migrating mysql DB\nMigrations complete \nServer starting on http://localhost:8080",
|
|
"title": "Configure"
|
|
},
|
|
{
|
|
"location": "/mysql/fluent/#query",
|
|
"text": "Now that you have created a model and a corresponding schema in your database, let's make your first query. router . get ( users ) { req in \n return User . query ( on : req ). all () } If you run your app, and query that route, you should see an empty array returned. Now you just need to add some users! Congratulations on getting your first Fluent MySQL model and migration working.",
|
|
"title": "Query"
|
|
},
|
|
{
|
|
"location": "/mysql/fluent/#connection",
|
|
"text": "With Fluent, you always have access to the underlying database driver. Using this underlying driver to perform a query is sometimes called a \"raw query\". Let's take a look at a raw MySQL query. router . get ( mysql-version ) { req - Future String in \n return req . withPooledConnection ( to : . mysql ) { conn in \n return try conn . query ( select @@version as v; ). map ( to : String . self ) { rows in \n return try rows [ 0 ]. firstValue ( forColumn : v )?. decode ( String . self ) ?? n/a \n } \n } } In the above example, withPooledConnection(to:) is used to create a connection to the database identified by .mysql . This is the default database identifier. See Fluent Database to learn more. Once we have the MySQLConnection , we can perform a query on it. You can learn more about the methods available in MySQL Core .",
|
|
"title": "Connection"
|
|
},
|
|
{
|
|
"location": "/mysql/core/",
|
|
"text": "MySQL Core\n\n\nMySQL (\nvapor/mysql\n) is a pure-Swift (no \nlibmysql\n dependency), event-driven, non-blocking driver for MySQL. It's built on top of the \nSwiftNIO\n networking library.\n\n\n\n\nSeealso\n\n\nThe higher-level, Fluent MySQL ORM guide is located at \nMySQL \n Fluent\n\n\n\n\nUsing just the MySQL package for your project may be a good idea if any of the following are true.\n\n\n\n\nYou have an existing DB with non-standard structure.\n\n\nYou rely heavily on custom or complex SQL queries.\n\n\nYou just plain don't like ORMs.\n\n\n\n\nMySQL core is built on top of DatabaseKit which provides some conveniences like connection pooling and integrations with Vapor's \nServices\n architecture.\n\n\n\n\nTip\n\n\nEven if you do choose to use \nFluent MySQL\n, all of the features of MySQL core will be available to you.\n\n\n\n\nGetting Started\n\n\nLet's take a look at how you can get started using MySQL core.\n\n\nPackage\n\n\nThe first step to using MySQL core is adding it as a dependency to your project in your SPM package manifest file.\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\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/// Any other dependencies ...\n\n\n \n// \ud83d\udc2c Non-blocking, event-driven Swift client for MySQL.\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/mysql.git\n,\n \nfrom\n:\n \n3.0.0-rc\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nApp\n,\n \ndependencies\n:\n \n[\nMySQL\n,\n \n...]),\n\n \n.\ntarget\n(\nname\n:\n \nRun\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n.\ntestTarget\n(\nname\n:\n \nAppTests\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n]\n\n\n)\n\n\n\n\n\n\nDon't forget to add the module as a dependency in the \ntargets\n array. Once you have added the dependency, regenerate your Xcode project with the following command:\n\n\nvapor xcode\n\n\n\n\n\nConfig\n\n\nThe next step is to configure the database in your \nconfigure.swift\n file.\n\n\nimport\n \nMySQL\n\n\n\n/// ...\n\n\n\n/// Register providers first\n\n\ntry\n \nservices\n.\nregister\n(\nMySQLProvider\n())\n\n\n\n\n\n\nRegistering the provider will add all of the services required for MySQL to work properly. It also includes a default database config struct that uses typical development environment credentials.\n\n\nYou can of course override this config struct if you have non-standard credentials.\n\n\n/// Register custom MySQL Config\n\n\nlet\n \nmysqlConfig\n \n=\n \nMySQLDatabaseConfig\n(\nhostname\n:\n \nlocalhost\n,\n \nport\n:\n \n3306\n,\n \nusername\n:\n \nvapor\n)\n\n\nservices\n.\nregister\n(\nmysqlConfig\n)\n\n\n\n\n\n\nQuery\n\n\nNow that the database is configured, you can make your first query.\n\n\nrouter\n.\nget\n(\nmysql-version\n)\n \n{\n \nreq\n \n-\n \nFuture\nString\n \nin\n\n \nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nmysql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nselect @@version as v;\n).\nmap\n(\nto\n:\n \nString\n.\nself\n)\n \n{\n \nrows\n \nin\n\n \nreturn\n \ntry\n \nrows\n[\n0\n].\nfirstValue\n(\nforColumn\n:\n \nv\n)?.\ndecode\n(\nString\n.\nself\n)\n \n??\n \nn/a\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nVisiting this route should display your MySQL version.\n\n\nConnection\n\n\nA \nMySQLConnection\n is normally created using the \nRequest\n container and can perform two different types of queries.\n\n\nCreate\n\n\nThere are a few methods for creating a \nMySQLConnection\n with a \nContainer\n (typically a \nRequest\n).\n\n\nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nmysql\n)\n \n{\n \nconn\n \nin\n\n \n/// ...\n\n\n}\n\n\nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nmysql\n)\n \n{\n \nconn\n \nin\n\n \n/// ...\n\n\n}\n\n\n\n\n\n\nAs the names imply, \nwithPooledConnection(to:)\n utilizes a connection pool. \nwithConnection(to:)\n does not. Connection pooling is a great way to ensure your application does not exceed the limits of your database, even under peak load.\n\n\nYou can also create a connection manually using \nMySQLDatabase.makeConnection(on:)\n and passing a \nWorker\n.\n\n\nSimply Query\n\n\nUse \n.simpleQuery(_:)\n to perform a query on your MySQL database that does not bind any parameters. Some queries you send to MySQL may actually require that you use the \nsimpleQuery(_:)\n method instead of the parameterized method.\n\n\n\n\nNote\n\n\nThis method sends and receives data as text-encoded, meaning it is not optimal for transmitting things like integers.\n\n\n\n\nlet\n \nrows\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nmysql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \nconn\n.\nsimpleQuery\n(\nSELECT * FROM users;\n)\n\n\n}\n\n\nprint\n(\nrows\n)\n \n// Future\n[[MySQLColumn: MySQLData]]\n\n\n\n\n\n\nYou can also choose to receive each row in a callback, which is great for conserving memory for large queries.\n\n\nlet\n \ndone\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nmysql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \nconn\n.\nsimpleQuery\n(\nSELECT * FROM users;\n)\n \n{\n \nrow\n \nin\n\n \nprint\n(\nrow\n)\n \n// [MySQLColumn: MySQLData]\n\n \n}\n\n\n}\n\n\nprint\n(\ndone\n)\n \n// Future\nVoid\n\n\n\n\n\n\nParameterized Query\n\n\nMySQL also supports sending parameterized queries (sometimes called prepared statements). This method allows you to insert data placeholders into the SQL string and send the values separately.\n\n\nData sent via parameterized queries is binary encoded, making it more efficient for sending some data types. In general, you should use parameterized queries where ever possible.\n\n\nlet\n \nusers\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nmysql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nSELECT * users WHERE name = $1;\n,\n \n[\nVapor\n])\n\n\n}\n\n\nprint\n(\nusers\n)\n \n// Future\n[[MySQLColumn: MySQLData]]\n\n\n\n\n\n\nYou can also provide a callback, similar to simple queries, for handling each row individually.",
|
|
"title": "MySQL Core"
|
|
},
|
|
{
|
|
"location": "/mysql/core/#mysql-core",
|
|
"text": "MySQL ( vapor/mysql ) is a pure-Swift (no libmysql dependency), event-driven, non-blocking driver for MySQL. It's built on top of the SwiftNIO networking library. Seealso The higher-level, Fluent MySQL ORM guide is located at MySQL Fluent Using just the MySQL package for your project may be a good idea if any of the following are true. You have an existing DB with non-standard structure. You rely heavily on custom or complex SQL queries. You just plain don't like ORMs. MySQL core is built on top of DatabaseKit which provides some conveniences like connection pooling and integrations with Vapor's Services architecture. Tip Even if you do choose to use Fluent MySQL , all of the features of MySQL core will be available to you.",
|
|
"title": "MySQL Core"
|
|
},
|
|
{
|
|
"location": "/mysql/core/#getting-started",
|
|
"text": "Let's take a look at how you can get started using MySQL core.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/mysql/core/#package",
|
|
"text": "The first step to using MySQL core is adding it as a dependency to your project in your SPM package manifest file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : MyApp , \n dependencies : [ \n /// Any other dependencies ... \n\n // \ud83d\udc2c Non-blocking, event-driven Swift client for MySQL. \n . package ( url : https://github.com/vapor/mysql.git , from : 3.0.0-rc ), \n ], \n targets : [ \n . target ( name : App , dependencies : [ MySQL , ...]), \n . target ( name : Run , dependencies : [ App ]), \n . testTarget ( name : AppTests , dependencies : [ App ]), \n ] ) Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command: vapor xcode",
|
|
"title": "Package"
|
|
},
|
|
{
|
|
"location": "/mysql/core/#config",
|
|
"text": "The next step is to configure the database in your configure.swift file. import MySQL /// ... /// Register providers first try services . register ( MySQLProvider ()) Registering the provider will add all of the services required for MySQL to work properly. It also includes a default database config struct that uses typical development environment credentials. You can of course override this config struct if you have non-standard credentials. /// Register custom MySQL Config let mysqlConfig = MySQLDatabaseConfig ( hostname : localhost , port : 3306 , username : vapor ) services . register ( mysqlConfig )",
|
|
"title": "Config"
|
|
},
|
|
{
|
|
"location": "/mysql/core/#query",
|
|
"text": "Now that the database is configured, you can make your first query. router . get ( mysql-version ) { req - Future String in \n return req . withPooledConnection ( to : . mysql ) { conn in \n return try conn . query ( select @@version as v; ). map ( to : String . self ) { rows in \n return try rows [ 0 ]. firstValue ( forColumn : v )?. decode ( String . self ) ?? n/a \n } \n } } Visiting this route should display your MySQL version.",
|
|
"title": "Query"
|
|
},
|
|
{
|
|
"location": "/mysql/core/#connection",
|
|
"text": "A MySQLConnection is normally created using the Request container and can perform two different types of queries.",
|
|
"title": "Connection"
|
|
},
|
|
{
|
|
"location": "/mysql/core/#create",
|
|
"text": "There are a few methods for creating a MySQLConnection with a Container (typically a Request ). return req . withPooledConnection ( to : . mysql ) { conn in \n /// ... } return req . withConnection ( to : . mysql ) { conn in \n /// ... } As the names imply, withPooledConnection(to:) utilizes a connection pool. withConnection(to:) does not. Connection pooling is a great way to ensure your application does not exceed the limits of your database, even under peak load. You can also create a connection manually using MySQLDatabase.makeConnection(on:) and passing a Worker .",
|
|
"title": "Create"
|
|
},
|
|
{
|
|
"location": "/mysql/core/#simply-query",
|
|
"text": "Use .simpleQuery(_:) to perform a query on your MySQL database that does not bind any parameters. Some queries you send to MySQL may actually require that you use the simpleQuery(_:) method instead of the parameterized method. Note This method sends and receives data as text-encoded, meaning it is not optimal for transmitting things like integers. let rows = req . withPooledConnection ( to : . mysql ) { conn in \n return conn . simpleQuery ( SELECT * FROM users; ) } print ( rows ) // Future [[MySQLColumn: MySQLData]] You can also choose to receive each row in a callback, which is great for conserving memory for large queries. let done = req . withPooledConnection ( to : . mysql ) { conn in \n return conn . simpleQuery ( SELECT * FROM users; ) { row in \n print ( row ) // [MySQLColumn: MySQLData] \n } } print ( done ) // Future Void",
|
|
"title": "Simply Query"
|
|
},
|
|
{
|
|
"location": "/mysql/core/#parameterized-query",
|
|
"text": "MySQL also supports sending parameterized queries (sometimes called prepared statements). This method allows you to insert data placeholders into the SQL string and send the values separately. Data sent via parameterized queries is binary encoded, making it more efficient for sending some data types. In general, you should use parameterized queries where ever possible. let users = req . withPooledConnection ( to : . mysql ) { conn in \n return try conn . query ( SELECT * users WHERE name = $1; , [ Vapor ]) } print ( users ) // Future [[MySQLColumn: MySQLData]] You can also provide a callback, similar to simple queries, for handling each row individually.",
|
|
"title": "Parameterized Query"
|
|
},
|
|
{
|
|
"location": "/postgresql/getting-started/",
|
|
"text": "Warning\n\n\nPostgreSQL 1.0 is still in beta. Some documentation may be missing or out of date.\n\n\n\n\nGetting Started with PostgreSQL\n\n\nPostgreSQL\n is a powerful, open-source database that puts an emphasis on standards compliance. PostgreSQL's strong type-system make it a great fit with Swift, and it is the preferred database for use with Vapor.\n\n\nYou can use PostgreSQL with Vapor (or any server-side Swift framework) by either:\n\n\n\n\nUsing \nFluent PostgreSQL\n ORM.\n\n\nUse just \nPostgreSQL core\n.\n\n\n\n\nWe recommend using the ORM since it does a lot of the hard work for you. Check out the respective guides to learn more.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/postgresql/getting-started/#getting-started-with-postgresql",
|
|
"text": "PostgreSQL is a powerful, open-source database that puts an emphasis on standards compliance. PostgreSQL's strong type-system make it a great fit with Swift, and it is the preferred database for use with Vapor. You can use PostgreSQL with Vapor (or any server-side Swift framework) by either: Using Fluent PostgreSQL ORM. Use just PostgreSQL core . We recommend using the ORM since it does a lot of the hard work for you. Check out the respective guides to learn more.",
|
|
"title": "Getting Started with PostgreSQL"
|
|
},
|
|
{
|
|
"location": "/postgresql/fluent/",
|
|
"text": "Fluent PostgreSQL\n\n\nFluent PostgreSQL (\nvapor/fluent-postgresql\n) is a type-safe, fast, and easy-to-use ORM for PostgreSQL built on top of \nFluent\n.\n\n\n\n\nSeealso\n\n\nThe Fluent PostgreSQL package is built on top of \nFluent\n and the pure Swift, NIO-based \nPostgreSQL core\n. You should refer to their guides for more information about subjects not covered here.\n\n\n\n\nGetting Started\n\n\nThis section will show you how to add Fluent PostgreSQL to your project and create your first \nPostgreSQLModel\n.\n\n\nPackage\n\n\nThe first step to using Fluent PostgreSQL is adding it as a dependency to your project in your SPM package manifest file.\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\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/// Any other dependencies ...\n\n\n \n// \ud83d\udd8b\ud83d\udc18 Swift ORM (queries, models, relations, etc) built on PostgreSQL.\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/fluent-postgresql.git\n,\n \nfrom\n:\n \n1.0.0-rc\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nApp\n,\n \ndependencies\n:\n \n[\nFluentPostgreSQL\n,\n \n...]),\n\n \n.\ntarget\n(\nname\n:\n \nRun\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n.\ntestTarget\n(\nname\n:\n \nAppTests\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n]\n\n\n)\n\n\n\n\n\n\nDon't forget to add the module as a dependency in the \ntargets\n array. Once you have added the dependency, regenerate your Xcode project with the following command:\n\n\nvapor xcode\n\n\n\n\n\nModel\n\n\nNow let's create our first \nPostgreSQLModel\n. Models represent tables in your PostgreSQL database and they are the primary method of interacting with your data. \n\n\nimport\n \nFluentPostgreSQL\n\n\nimport\n \nVapor\n\n\n\n/// A simple user.\n\n\nfinal\n \nclass\n \nUser\n:\n \nPostgreSQLModel\n \n{\n\n \n/// The unique identifier for this user.\n\n \nvar\n \nid\n:\n \nInt\n?\n\n\n \n/// The user\ns full name.\n\n \nvar\n \nname\n:\n \nString\n\n\n \n/// The user\ns current age in years.\n\n \nvar\n \nage\n:\n \nInt\n\n\n \n/// Creates a new user.\n\n \ninit\n(\nid\n:\n \nInt\n?\n \n=\n \nnil\n,\n \nname\n:\n \nString\n,\n \nage\n:\n \nInt\n)\n \n{\n\n \nself\n.\nid\n \n=\n \nid\n\n \nself\n.\nname\n \n=\n \nname\n\n \nself\n.\nage\n \n=\n \nage\n\n \n}\n\n\n}\n\n\n\n\n\n\nThe example above shows a \nPostgreSQLModel\n for a simple model representing a user. You can make both \nstruct\ns and \nclass\nes a model. You can even conform types that come from external modules. The only requirement is that these types conform to \nCodable\n, which must be declared on the base type for synthesized (automatic) conformance.\n\n\nStandard practice with PostgreSQL databases is using an auto-generated \nINTEGER\n for creating and storing unique identifiers in the \nid\n column. It's also possible to use \nUUID\ns or even \nString\ns for your identifiers. There are convenience protocol for that. \n\n\n\n\n\n\n\n\nprotocol\n\n\ntype\n\n\nkey\n\n\n\n\n\n\n\n\n\n\nPostgreSQLModel\n\n\nInt\n\n\nid\n\n\n\n\n\n\nPostgreSQLUUIDModel\n\n\nUUID\n\n\nid\n\n\n\n\n\n\nPostgreSQLStringModel\n\n\nString\n\n\nid\n\n\n\n\n\n\n\n\n\n\nSeealso\n\n\nTake a look at \nFluent \n Model\n for more information on creating models with custom ID types and keys.\n\n\n\n\nMigration\n\n\nAll of your models (with some rare exceptions) should have a corresponding table\nor \nschema\nin your database. You can use a \nFluent \n Migration\n to automatically generate this schema in a testable, maintainable way. Fluent makes it easy to automatically generate a migration for your model\n\n\n\n\nTip\n\n\nIf you are creating models to represent an existing table or database, you can skip this step.\n\n\n\n\n/// Allows `User` to be used as a migration.\n\n\nextension\n \nUser\n:\n \nMigration\n \n{\n \n}\n\n\n\n\n\n\nThat's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it.\n\n\nTake a look at \nFluent \n Migration\n if you are interested in customizing this migration.\n\n\nConfigure\n\n\nThe final step is to configure your database. At a minimum, this requires adding two things to your \nconfigure.swift\n file.\n\n\n\n\nFluentPostgreSQLProvider\n\n\nMigrationConfig\n\n\n\n\nLet's take a look.\n\n\nimport\n \nFluentPostgreSQL\n\n\n\n/// ...\n\n\n\n/// Register providers first\n\n\ntry\n \nservices\n.\nregister\n(\nFluentPostgreSQLProvider\n())\n\n\n\n/// Configure migrations\n\n\nvar\n \nmigrations\n \n=\n \nMigrationConfig\n()\n\n\nmigrations\n.\nadd\n(\nmodel\n:\n \nUser\n.\nself\n,\n \ndatabase\n:\n \n.\npsql\n)\n\n\nservices\n.\nregister\n(\nmigrations\n)\n\n\n\n/// Other services....\n\n\n\n\n\n\nRegistering the provider will add all of the services required for Fluent PostgreSQL to work properly. It also includes a default database config struct that uses typical development environment credentials. \n\n\nYou can of course override this config struct if you have non-standard credentials.\n\n\n/// Register custom PostgreSQL Config\n\n\nlet\n \npsqlConfig\n \n=\n \nPostgreSQLDatabaseConfig\n(\nhostname\n:\n \nlocalhost\n,\n \nport\n:\n \n5432\n,\n \nusername\n:\n \nvapor\n)\n\n\nservices\n.\nregister\n(\npsqlConfig\n)\n\n\n\n\n\n\nOnce you have the \nMigrationConfig\n added, you should be able to run your application and see the following:\n\n\nMigrating psql DB\nMigrations \ncomplete\n\nServer starting on http://localhost:8080\n\n\n\n\n\nQuery\n\n\nNow that you have created a model and a corresponding schema in your database, let's make your first query.\n\n\nrouter\n.\nget\n(\nusers\n)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nUser\n.\nquery\n(\non\n:\n \nreq\n).\nall\n()\n\n\n}\n\n\n\n\n\n\nIf you run your app, and query that route, you should see an empty array returned. Now you just need to add some users! Congratulations on getting your first Fluent PostgreSQL model and migration working.\n\n\nConnection\n\n\nWith Fluent, you always have access to the underlying database driver. Using this underlying driver to perform a query is sometimes called a \"raw query\".\n\n\nLet's take a look at a raw PostgreSQL query.\n\n\nrouter\n.\nget\n(\npsql-version\n)\n \n{\n \nreq\n \n-\n \nFuture\nString\n \nin\n\n \nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\npsql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nselect version() as v;\n).\nmap\n(\nto\n:\n \nString\n.\nself\n)\n \n{\n \nrows\n \nin\n\n \nreturn\n \ntry\n \nrows\n[\n0\n].\nfirstValue\n(\nforColumn\n:\n \nv\n)?.\ndecode\n(\nString\n.\nself\n)\n \n??\n \nn/a\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nIn the above example, \nwithPooledConnection(to:)\n is used to create a connection to the database identified by \n.psql\n. This is the default database identifier. See \nFluent \n Database\n to learn more.\n\n\nOnce we have the \nPostgreSQLConnection\n, we can perform a query on it. You can learn more about the methods available in \nPostgreSQL \n Core\n.",
|
|
"title": "Fluent PostgreSQL"
|
|
},
|
|
{
|
|
"location": "/postgresql/fluent/#fluent-postgresql",
|
|
"text": "Fluent PostgreSQL ( vapor/fluent-postgresql ) is a type-safe, fast, and easy-to-use ORM for PostgreSQL built on top of Fluent . Seealso The Fluent PostgreSQL package is built on top of Fluent and the pure Swift, NIO-based PostgreSQL core . You should refer to their guides for more information about subjects not covered here.",
|
|
"title": "Fluent PostgreSQL"
|
|
},
|
|
{
|
|
"location": "/postgresql/fluent/#getting-started",
|
|
"text": "This section will show you how to add Fluent PostgreSQL to your project and create your first PostgreSQLModel .",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/postgresql/fluent/#package",
|
|
"text": "The first step to using Fluent PostgreSQL is adding it as a dependency to your project in your SPM package manifest file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : MyApp , \n dependencies : [ \n /// Any other dependencies ... \n\n // \ud83d\udd8b\ud83d\udc18 Swift ORM (queries, models, relations, etc) built on PostgreSQL. \n . package ( url : https://github.com/vapor/fluent-postgresql.git , from : 1.0.0-rc ), \n ], \n targets : [ \n . target ( name : App , dependencies : [ FluentPostgreSQL , ...]), \n . target ( name : Run , dependencies : [ App ]), \n . testTarget ( name : AppTests , dependencies : [ App ]), \n ] ) Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command: vapor xcode",
|
|
"title": "Package"
|
|
},
|
|
{
|
|
"location": "/postgresql/fluent/#model",
|
|
"text": "Now let's create our first PostgreSQLModel . Models represent tables in your PostgreSQL database and they are the primary method of interacting with your data. import FluentPostgreSQL import Vapor /// A simple user. final class User : PostgreSQLModel { \n /// The unique identifier for this user. \n var id : Int ? \n\n /// The user s full name. \n var name : String \n\n /// The user s current age in years. \n var age : Int \n\n /// Creates a new user. \n init ( id : Int ? = nil , name : String , age : Int ) { \n self . id = id \n self . name = name \n self . age = age \n } } The example above shows a PostgreSQLModel for a simple model representing a user. You can make both struct s and class es a model. You can even conform types that come from external modules. The only requirement is that these types conform to Codable , which must be declared on the base type for synthesized (automatic) conformance. Standard practice with PostgreSQL databases is using an auto-generated INTEGER for creating and storing unique identifiers in the id column. It's also possible to use UUID s or even String s for your identifiers. There are convenience protocol for that. protocol type key PostgreSQLModel Int id PostgreSQLUUIDModel UUID id PostgreSQLStringModel String id Seealso Take a look at Fluent Model for more information on creating models with custom ID types and keys.",
|
|
"title": "Model"
|
|
},
|
|
{
|
|
"location": "/postgresql/fluent/#migration",
|
|
"text": "All of your models (with some rare exceptions) should have a corresponding table or schema in your database. You can use a Fluent Migration to automatically generate this schema in a testable, maintainable way. Fluent makes it easy to automatically generate a migration for your model Tip If you are creating models to represent an existing table or database, you can skip this step. /// Allows `User` to be used as a migration. extension User : Migration { } That's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it. Take a look at Fluent Migration if you are interested in customizing this migration.",
|
|
"title": "Migration"
|
|
},
|
|
{
|
|
"location": "/postgresql/fluent/#configure",
|
|
"text": "The final step is to configure your database. At a minimum, this requires adding two things to your configure.swift file. FluentPostgreSQLProvider MigrationConfig Let's take a look. import FluentPostgreSQL /// ... /// Register providers first try services . register ( FluentPostgreSQLProvider ()) /// Configure migrations var migrations = MigrationConfig () migrations . add ( model : User . self , database : . psql ) services . register ( migrations ) /// Other services.... Registering the provider will add all of the services required for Fluent PostgreSQL to work properly. It also includes a default database config struct that uses typical development environment credentials. You can of course override this config struct if you have non-standard credentials. /// Register custom PostgreSQL Config let psqlConfig = PostgreSQLDatabaseConfig ( hostname : localhost , port : 5432 , username : vapor ) services . register ( psqlConfig ) Once you have the MigrationConfig added, you should be able to run your application and see the following: Migrating psql DB\nMigrations complete \nServer starting on http://localhost:8080",
|
|
"title": "Configure"
|
|
},
|
|
{
|
|
"location": "/postgresql/fluent/#query",
|
|
"text": "Now that you have created a model and a corresponding schema in your database, let's make your first query. router . get ( users ) { req in \n return User . query ( on : req ). all () } If you run your app, and query that route, you should see an empty array returned. Now you just need to add some users! Congratulations on getting your first Fluent PostgreSQL model and migration working.",
|
|
"title": "Query"
|
|
},
|
|
{
|
|
"location": "/postgresql/fluent/#connection",
|
|
"text": "With Fluent, you always have access to the underlying database driver. Using this underlying driver to perform a query is sometimes called a \"raw query\". Let's take a look at a raw PostgreSQL query. router . get ( psql-version ) { req - Future String in \n return req . withPooledConnection ( to : . psql ) { conn in \n return try conn . query ( select version() as v; ). map ( to : String . self ) { rows in \n return try rows [ 0 ]. firstValue ( forColumn : v )?. decode ( String . self ) ?? n/a \n } \n } } In the above example, withPooledConnection(to:) is used to create a connection to the database identified by .psql . This is the default database identifier. See Fluent Database to learn more. Once we have the PostgreSQLConnection , we can perform a query on it. You can learn more about the methods available in PostgreSQL Core .",
|
|
"title": "Connection"
|
|
},
|
|
{
|
|
"location": "/postgresql/core/",
|
|
"text": "PostgreSQL Core\n\n\nPostgreSQL (\nvapor/postgresql\n) is a pure-Swift (no \nlibpq\n dependency), event-driven, non-blocking driver for PostgreSQL. It's built on top of the \nSwiftNIO\n networking library.\n\n\n\n\nSeealso\n\n\nThe higher-level, Fluent PostgreSQL ORM guide is located at \nPostgreSQL \n Fluent\n\n\n\n\nUsing just the PostgreSQL package for your project may be a good idea if any of the following are true.\n\n\n\n\nYou have an existing DB with non-standard structure.\n\n\nYou rely heavily on custom or complex SQL queries.\n\n\nYou just plain don't like ORMs.\n\n\n\n\nPostgreSQL core is built on top of DatabaseKit which provides some conveniences like connection pooling and integrations with Vapor's \nServices\n architecture.\n\n\n\n\nTip\n\n\nEven if you do choose to use \nFluent PostgreSQL\n, all of the features of PostgreSQL core will be available to you.\n\n\n\n\nGetting Started\n\n\nLet's take a look at how you can get started using PostgreSQL core.\n\n\nPackage\n\n\nThe first step to using PostgreSQL core is adding it as a dependency to your project in your SPM package manifest file.\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\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/// Any other dependencies ...\n\n\n \n// \ud83d\udc18 Non-blocking, event-driven Swift client for PostgreSQL.\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/postgresql.git\n,\n \nfrom\n:\n \n1.0.0-rc\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nApp\n,\n \ndependencies\n:\n \n[\nPostgreSQL\n,\n \n...]),\n\n \n.\ntarget\n(\nname\n:\n \nRun\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n.\ntestTarget\n(\nname\n:\n \nAppTests\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n]\n\n\n)\n\n\n\n\n\n\nDon't forget to add the module as a dependency in the \ntargets\n array. Once you have added the dependency, regenerate your Xcode project with the following command:\n\n\nvapor xcode\n\n\n\n\n\nConfig\n\n\nThe next step is to configure the database in your \nconfigure.swift\n file.\n\n\nimport\n \nPostgreSQL\n\n\n\n/// ...\n\n\n\n/// Register providers first\n\n\ntry\n \nservices\n.\nregister\n(\nPostgreSQLProvider\n())\n\n\n\n\n\n\nRegistering the provider will add all of the services required for PostgreSQL to work properly. It also includes a default database config struct that uses typical development environment credentials. \n\n\nYou can of course override this config struct if you have non-standard credentials.\n\n\n/// Register custom PostgreSQL Config\n\n\nlet\n \npsqlConfig\n \n=\n \nPostgreSQLDatabaseConfig\n(\nhostname\n:\n \nlocalhost\n,\n \nport\n:\n \n5432\n,\n \nusername\n:\n \nvapor\n)\n\n\nservices\n.\nregister\n(\npsqlConfig\n)\n\n\n\n\n\n\nQuery\n\n\nNow that the database is configured, you can make your first query.\n\n\nrouter\n.\nget\n(\npsql-version\n)\n \n{\n \nreq\n \n-\n \nFuture\nString\n \nin\n\n \nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\npsql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nselect version() as v;\n).\nmap\n(\nto\n:\n \nString\n.\nself\n)\n \n{\n \nrows\n \nin\n\n \nreturn\n \ntry\n \nrows\n[\n0\n].\nfirstValue\n(\nforColumn\n:\n \nv\n)?.\ndecode\n(\nString\n.\nself\n)\n \n??\n \nn/a\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nVisiting this route should display your PostgreSQL version.\n\n\nConnection\n\n\nA \nPostgreSQLConnection\n is normally created using the \nRequest\n container and can perform two different types of queries.\n\n\nCreate\n\n\nThere are two methods for creating a \nPostgreSQLConnection\n.\n\n\nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\npsql\n)\n \n{\n \nconn\n \nin\n\n \n/// ...\n\n\n}\n\n\nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\npsql\n)\n \n{\n \nconn\n \nin\n\n \n/// ...\n\n\n}\n\n\n\n\n\n\nAs the names imply, \nwithPooledConnection(to:)\n utilizes a connection pool. \nwithConnection(to:)\n does not. Connection pooling is a great way to ensure your application does not exceed the limits of your database, even under peak load.\n\n\nSimply Query\n\n\nUse \n.simpleQuery(_:)\n to perform a query on your PostgreSQL database that does not bind any parameters. Some queries you send to PostgreSQL may actually require that you use the \nsimpleQuery(_:)\n method instead of the parameterized method. \n\n\n\n\nNote\n\n\nThis method sends and receives data as text-encoded, meaning it is not optimal for transmitting things like integers.\n\n\n\n\nlet\n \nrows\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\npsql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \nconn\n.\nsimpleQuery\n(\nSELECT * FROM users;\n)\n\n\n}\n\n\nprint\n(\nrows\n)\n \n// Future\n[[PostgreSQLColumn: PostgreSQLData]]\n\n\n\n\n\n\nYou can also choose to receive each row in a callback, which is great for conserving memory for large queries.\n\n\nlet\n \ndone\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\npsql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \nconn\n.\nsimpleQuery\n(\nSELECT * FROM users;\n)\n \n{\n \nrow\n \nin\n\n \nprint\n(\nrow\n)\n \n// [PostgreSQLColumn: PostgreSQLData]\n\n \n}\n\n\n}\n\n\nprint\n(\ndone\n)\n \n// Future\nVoid\n\n\n\n\n\n\nParameterized Query\n\n\nPostgreSQL also supports sending parameterized queries (sometimes called prepared statements). This method allows you to insert data placeholders into the SQL string and send the values separately.\n\n\nData sent via parameterized queries is binary encoded, making it more efficient for sending some data types. In general, you should use parameterized queries where ever possible.\n\n\nlet\n \nusers\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\npsql\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nSELECT * users WHERE name = $1;\n,\n \n[\nVapor\n])\n\n\n}\n\n\nprint\n(\nusers\n)\n \n// Future\n[[PostgreSQLColumn: PostgreSQLData]]\n\n\n\n\n\n\nYou can also provide a callback, similar to simple queries, for handling each row individually.",
|
|
"title": "PostgreSQL Core"
|
|
},
|
|
{
|
|
"location": "/postgresql/core/#postgresql-core",
|
|
"text": "PostgreSQL ( vapor/postgresql ) is a pure-Swift (no libpq dependency), event-driven, non-blocking driver for PostgreSQL. It's built on top of the SwiftNIO networking library. Seealso The higher-level, Fluent PostgreSQL ORM guide is located at PostgreSQL Fluent Using just the PostgreSQL package for your project may be a good idea if any of the following are true. You have an existing DB with non-standard structure. You rely heavily on custom or complex SQL queries. You just plain don't like ORMs. PostgreSQL core is built on top of DatabaseKit which provides some conveniences like connection pooling and integrations with Vapor's Services architecture. Tip Even if you do choose to use Fluent PostgreSQL , all of the features of PostgreSQL core will be available to you.",
|
|
"title": "PostgreSQL Core"
|
|
},
|
|
{
|
|
"location": "/postgresql/core/#getting-started",
|
|
"text": "Let's take a look at how you can get started using PostgreSQL core.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/postgresql/core/#package",
|
|
"text": "The first step to using PostgreSQL core is adding it as a dependency to your project in your SPM package manifest file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : MyApp , \n dependencies : [ \n /// Any other dependencies ... \n\n // \ud83d\udc18 Non-blocking, event-driven Swift client for PostgreSQL. \n . package ( url : https://github.com/vapor/postgresql.git , from : 1.0.0-rc ), \n ], \n targets : [ \n . target ( name : App , dependencies : [ PostgreSQL , ...]), \n . target ( name : Run , dependencies : [ App ]), \n . testTarget ( name : AppTests , dependencies : [ App ]), \n ] ) Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command: vapor xcode",
|
|
"title": "Package"
|
|
},
|
|
{
|
|
"location": "/postgresql/core/#config",
|
|
"text": "The next step is to configure the database in your configure.swift file. import PostgreSQL /// ... /// Register providers first try services . register ( PostgreSQLProvider ()) Registering the provider will add all of the services required for PostgreSQL to work properly. It also includes a default database config struct that uses typical development environment credentials. You can of course override this config struct if you have non-standard credentials. /// Register custom PostgreSQL Config let psqlConfig = PostgreSQLDatabaseConfig ( hostname : localhost , port : 5432 , username : vapor ) services . register ( psqlConfig )",
|
|
"title": "Config"
|
|
},
|
|
{
|
|
"location": "/postgresql/core/#query",
|
|
"text": "Now that the database is configured, you can make your first query. router . get ( psql-version ) { req - Future String in \n return req . withPooledConnection ( to : . psql ) { conn in \n return try conn . query ( select version() as v; ). map ( to : String . self ) { rows in \n return try rows [ 0 ]. firstValue ( forColumn : v )?. decode ( String . self ) ?? n/a \n } \n } } Visiting this route should display your PostgreSQL version.",
|
|
"title": "Query"
|
|
},
|
|
{
|
|
"location": "/postgresql/core/#connection",
|
|
"text": "A PostgreSQLConnection is normally created using the Request container and can perform two different types of queries.",
|
|
"title": "Connection"
|
|
},
|
|
{
|
|
"location": "/postgresql/core/#create",
|
|
"text": "There are two methods for creating a PostgreSQLConnection . return req . withPooledConnection ( to : . psql ) { conn in \n /// ... } return req . withConnection ( to : . psql ) { conn in \n /// ... } As the names imply, withPooledConnection(to:) utilizes a connection pool. withConnection(to:) does not. Connection pooling is a great way to ensure your application does not exceed the limits of your database, even under peak load.",
|
|
"title": "Create"
|
|
},
|
|
{
|
|
"location": "/postgresql/core/#simply-query",
|
|
"text": "Use .simpleQuery(_:) to perform a query on your PostgreSQL database that does not bind any parameters. Some queries you send to PostgreSQL may actually require that you use the simpleQuery(_:) method instead of the parameterized method. Note This method sends and receives data as text-encoded, meaning it is not optimal for transmitting things like integers. let rows = req . withPooledConnection ( to : . psql ) { conn in \n return conn . simpleQuery ( SELECT * FROM users; ) } print ( rows ) // Future [[PostgreSQLColumn: PostgreSQLData]] You can also choose to receive each row in a callback, which is great for conserving memory for large queries. let done = req . withPooledConnection ( to : . psql ) { conn in \n return conn . simpleQuery ( SELECT * FROM users; ) { row in \n print ( row ) // [PostgreSQLColumn: PostgreSQLData] \n } } print ( done ) // Future Void",
|
|
"title": "Simply Query"
|
|
},
|
|
{
|
|
"location": "/postgresql/core/#parameterized-query",
|
|
"text": "PostgreSQL also supports sending parameterized queries (sometimes called prepared statements). This method allows you to insert data placeholders into the SQL string and send the values separately. Data sent via parameterized queries is binary encoded, making it more efficient for sending some data types. In general, you should use parameterized queries where ever possible. let users = req . withPooledConnection ( to : . psql ) { conn in \n return try conn . query ( SELECT * users WHERE name = $1; , [ Vapor ]) } print ( users ) // Future [[PostgreSQLColumn: PostgreSQLData]] You can also provide a callback, similar to simple queries, for handling each row individually.",
|
|
"title": "Parameterized Query"
|
|
},
|
|
{
|
|
"location": "/redis/getting-started/",
|
|
"text": "Warning\n\n\nRedis 3.0 is still in beta. Some documentation may be missing or out of date.\n\n\n\n\nRedis\n\n\nRedis is a Redis client library that can communicate with a Redis database.\n\n\nWhat is Redis?\n\n\nRedis is an in-memory data store used as a database, cache and message broker. It supports most common data structures. Redis is most commonly used for caching data such as sessions and notifications (between multiple servers).\n\n\nRedis works as a key-value store, but allows querying the keys, unlike most databases.\n\n\nWith and without Vapor\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\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 \n.\nupToNextMajor\n(\nfrom\n:\n \n3.0.0\n)),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nRedis\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nIf this is your first time adding a dependency, you should read our introduction to \nPackage.swift\n.\n\n\nUse \nimport Redis\n to access Redis' APIs.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/redis/getting-started/#redis",
|
|
"text": "Redis is a Redis client library that can communicate with a Redis database.",
|
|
"title": "Redis"
|
|
},
|
|
{
|
|
"location": "/redis/getting-started/#what-is-redis",
|
|
"text": "Redis is an in-memory data store used as a database, cache and message broker. It supports most common data structures. Redis is most commonly used for caching data such as sessions and notifications (between multiple servers). Redis works as a key-value store, but allows querying the keys, unlike most databases.",
|
|
"title": "What is Redis?"
|
|
},
|
|
{
|
|
"location": "/redis/getting-started/#with-and-without-vapor",
|
|
"text": "To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . package ( url : https://github.com/vapor/redis.git , . upToNextMajor ( from : 3.0.0 )), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ Redis , ... ]) \n ] ) If this is your first time adding a dependency, you should read our introduction to Package.swift . Use import Redis to access Redis' APIs.",
|
|
"title": "With and without Vapor"
|
|
},
|
|
{
|
|
"location": "/redis/basics/",
|
|
"text": "Redis basic usage\n\n\nTo interact with Redis, you first need to construct a Redis client.\nThe Redis library primarily supports TCP sockets.\n\n\nThis requires a hostname, port and worker. The eventloop will be used for Redis' Socket. The hostname and port have a default. The hostname is defaulted to \nlocalhost\n, and the port to Redis' default port \n6379\n.\n\n\nlet\n \nclient\n \n=\n \ntry\n \nRedisClient\n.\nconnect\n(\non\n:\n \nworker\n)\n \n// Future\nRedisClient\n\n\n\n\n\n\nThe \nconnect\n method will return a future containing the TCP based Redis Client.\n\n\nRedis Data Types\n\n\nRedis has 6 data types:\n\n\n\n\nnull\n\n\nInt\n\n\nError\n\n\nArray\n\n\nBasic String (used for command names and basic replies only)\n\n\nBulk String (used for Strings and binary data blobs)\n\n\n\n\nYou can instantiate one from the static functions and variables on \nRedisData\n.\n\n\nlet\n \nnull\n \n=\n \nRedisData\n.\nnull\n\n\n\nlet\n \nhelloWorld\n \n=\n \nRedisData\n.\nbulkString\n(\nHello World\n)\n\n\n\nlet\n \nthree\n \n=\n \nRedisData\n.\ninteger\n(\n3\n)\n\n\n\nlet\n \noneThroughTen\n \n=\n \nRedisData\n.\narray\n([\n\n \n.\ninteger\n(\n1\n),\n\n \n.\ninteger\n(\n2\n),\n\n \n.\ninteger\n(\n3\n),\n\n \n.\ninteger\n(\n4\n),\n\n \n.\ninteger\n(\n5\n),\n\n \n.\ninteger\n(\n6\n),\n\n \n.\ninteger\n(\n7\n),\n\n \n.\ninteger\n(\n8\n),\n\n \n.\ninteger\n(\n9\n),\n\n \n.\ninteger\n(\n10\n)\n\n\n])\n\n\n\n\n\n\nThe above is the explicit way of defining Redis Types. You can also use literals in most scenarios:\n\n\nlet\n \narray\n \n=\n \nRedisData\n.\narray\n([\n\n \n[\n\n \n1\n,\n \n2\n,\n \n3\n,\n \n4\n,\n \n5\n,\n \n6\n,\n \n7\n,\n \n8\n,\n \n9\n,\n \n10\n\n \n],\n\n \nHello World\n,\n\n \nOne\n,\n\n \nTwo\n,\n\n \n.\nnull\n,\n\n \n.\nnull\n,\n\n \ntest\n\n\n])\n\n\n\n\n\n\nCRUD using Redis\n\n\nFrom here on it is assumed that your client has been successfully created and is available in the variable \nclient\n as a \nRedisClient\n.\n\n\nCreating a record\n\n\nCreating a record is done using a \nRedisData\n for a value and a key.\n\n\nclient\n.\nset\n(\nworld\n,\n \nforKey\n:\n \nhello\n)\n\n\n\n\n\n\nThis returns a future that'll indicate successful or unsuccessful insertion.\n\n\nReading a record\n\n\nReading a record is similar, only you'll get a warning if you don't use the returned future.\n\n\nThe \nFuture\nRedisData\n for the key \"hello\" will be \"world\" if you created the record as shown above.\n\n\nlet\n \nfutureRecord\n \n=\n \nclient\n.\ngetData\n(\nforKey\n:\n \nhello\n)\n \n// Future\nRedisData\n\n\n\n\n\n\nDeleting a record\n\n\nDeleting a record is similar but allows querying the keys, too.\n\n\nclient\n.\ndelete\n(\nkeys\n:\n \n[\nhello\n])\n\n\n\n\n\n\nWhere the above command will remove the key \"hello\", the next command will delete \nall\n keys from the Redis database.\n\n\nclient\n.\ndelete\n(\nkeys\n:\n \n[\n*\n])",
|
|
"title": "Basics"
|
|
},
|
|
{
|
|
"location": "/redis/basics/#redis-basic-usage",
|
|
"text": "To interact with Redis, you first need to construct a Redis client.\nThe Redis library primarily supports TCP sockets. This requires a hostname, port and worker. The eventloop will be used for Redis' Socket. The hostname and port have a default. The hostname is defaulted to localhost , and the port to Redis' default port 6379 . let client = try RedisClient . connect ( on : worker ) // Future RedisClient The connect method will return a future containing the TCP based Redis Client.",
|
|
"title": "Redis basic usage"
|
|
},
|
|
{
|
|
"location": "/redis/basics/#redis-data-types",
|
|
"text": "Redis has 6 data types: null Int Error Array Basic String (used for command names and basic replies only) Bulk String (used for Strings and binary data blobs) You can instantiate one from the static functions and variables on RedisData . let null = RedisData . null let helloWorld = RedisData . bulkString ( Hello World ) let three = RedisData . integer ( 3 ) let oneThroughTen = RedisData . array ([ \n . integer ( 1 ), \n . integer ( 2 ), \n . integer ( 3 ), \n . integer ( 4 ), \n . integer ( 5 ), \n . integer ( 6 ), \n . integer ( 7 ), \n . integer ( 8 ), \n . integer ( 9 ), \n . integer ( 10 ) ]) The above is the explicit way of defining Redis Types. You can also use literals in most scenarios: let array = RedisData . array ([ \n [ \n 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 \n ], \n Hello World , \n One , \n Two , \n . null , \n . null , \n test ])",
|
|
"title": "Redis Data Types"
|
|
},
|
|
{
|
|
"location": "/redis/basics/#crud-using-redis",
|
|
"text": "From here on it is assumed that your client has been successfully created and is available in the variable client as a RedisClient .",
|
|
"title": "CRUD using Redis"
|
|
},
|
|
{
|
|
"location": "/redis/basics/#creating-a-record",
|
|
"text": "Creating a record is done using a RedisData for a value and a key. client . set ( world , forKey : hello ) This returns a future that'll indicate successful or unsuccessful insertion.",
|
|
"title": "Creating a record"
|
|
},
|
|
{
|
|
"location": "/redis/basics/#reading-a-record",
|
|
"text": "Reading a record is similar, only you'll get a warning if you don't use the returned future. The Future RedisData for the key \"hello\" will be \"world\" if you created the record as shown above. let futureRecord = client . getData ( forKey : hello ) // Future RedisData",
|
|
"title": "Reading a record"
|
|
},
|
|
{
|
|
"location": "/redis/basics/#deleting-a-record",
|
|
"text": "Deleting a record is similar but allows querying the keys, too. client . delete ( keys : [ hello ]) Where the above command will remove the key \"hello\", the next command will delete all keys from the Redis database. client . delete ( keys : [ * ])",
|
|
"title": "Deleting a record"
|
|
},
|
|
{
|
|
"location": "/redis/custom-commands/",
|
|
"text": "Custom commands\n\n\nMany commands are not (yet) implemented by the driver using a convenience function. This does not mean the feature/command is not usable.\n\n\n(Almost) all functions listed here\n work out of the box using custom commands.\n\n\nUsage\n\n\nThe Redis client has a \nrun\n function that allows you to run these commands.\n\n\nThe following code demonstrates a \"custom\" implementation for \nGET\n.\n\n\nlet\n \nfuture\n \n=\n \nclient\n.\nrun\n(\ncommand\n:\n \nGET\n,\n \narguments\n:\n \n[\nmy-key\n])\n \n// Future\nRedisData\n\n\n\n\n\n\nThis future will contain the result as specified in the article on the redis command page or an error.",
|
|
"title": "Custom commands"
|
|
},
|
|
{
|
|
"location": "/redis/custom-commands/#custom-commands",
|
|
"text": "Many commands are not (yet) implemented by the driver using a convenience function. This does not mean the feature/command is not usable. (Almost) all functions listed here work out of the box using custom commands.",
|
|
"title": "Custom commands"
|
|
},
|
|
{
|
|
"location": "/redis/custom-commands/#usage",
|
|
"text": "The Redis client has a run function that allows you to run these commands. The following code demonstrates a \"custom\" implementation for GET . let future = client . run ( command : GET , arguments : [ my-key ]) // Future RedisData This future will contain the result as specified in the article on the redis command page or an error.",
|
|
"title": "Usage"
|
|
},
|
|
{
|
|
"location": "/redis/pub-sub/",
|
|
"text": "Publish \n Subscribe\n\n\nRedis' Publish and Subscribe model is really useful for notifications.\n\n\nUse cases\n\n\nPub/sub is used for notifying subscribers of an event.\nA simple and common event for example would be a chat message.\n\n\nA channel consists of a name and group of listeners. Think of it as being \n[String: [Listener]]\n.\nWhen you send a notification to a channel you need to provide a payload.\nEach listener will get a notification consisting of this payload.\n\n\nChannels must be a string. For chat groups, for example, you could use the database identifier.\n\n\nPublishing\n\n\nYou cannot get a list of listeners, but sending a payload will emit the amount of listeners that received the notification.\nSending (publishing) an event is done like so:\n\n\n// Any redis data\n\n\nlet\n \nnotification\n:\n \nRedisData\n \n=\n \nMy-Notification\n\n\n\nclient\n.\npublish\n(\nnotification\n,\n \nto\n:\n \nmy-channel\n)\n\n\n\n\n\n\nIf you want access to the listener count:\n\n\nlet\n \nnotifiedCount\n \n=\n \nclient\n.\npublish\n(\nnotification\n,\n \nto\n:\n \nmy-channel\n)\n \n// Future\nInt\n\n\n\n\n\n\nSubscribing\n\n\nTo subscribe for notifications you're rendering an entire Redis Client useless in exchange for listening to events.\n\n\nA single client can listen to one or more channels, which is provided using a set of unique channel names. The result of subscribing is a \nSubscriptionStream\n.\n\n\nlet\n \nnotifications\n \n=\n \nclient\n.\nsubscribe\n(\nto\n:\n \n[\nsome-notification-channel\n,\n \nother-notification-channel\n])\n\n\n\n\n\n\nIf you try to use the client after subscribing, all operations will fail. These errors are usually emitted through the Future.\n\n\nThis stream will receive messages asynchronously from the point of \ndraining\n. This works like any other async stream.\n\n\nNotifications consist of the channel and payload.\n\n\nnotifications\n.\ndrain\n \n{\n \nnotification\n \nin\n\n \nprint\n(\nnotification\n.\nchannel\n)\n\n\n \nlet\n \npayload\n \n=\n \nnotification\n.\npayload\n\n\n \n// \nTODO:\n Process the payload\n\n\n}",
|
|
"title": "Publish and Subscribe"
|
|
},
|
|
{
|
|
"location": "/redis/pub-sub/#publish-subscribe",
|
|
"text": "Redis' Publish and Subscribe model is really useful for notifications.",
|
|
"title": "Publish & Subscribe"
|
|
},
|
|
{
|
|
"location": "/redis/pub-sub/#use-cases",
|
|
"text": "Pub/sub is used for notifying subscribers of an event.\nA simple and common event for example would be a chat message. A channel consists of a name and group of listeners. Think of it as being [String: [Listener]] .\nWhen you send a notification to a channel you need to provide a payload.\nEach listener will get a notification consisting of this payload. Channels must be a string. For chat groups, for example, you could use the database identifier.",
|
|
"title": "Use cases"
|
|
},
|
|
{
|
|
"location": "/redis/pub-sub/#publishing",
|
|
"text": "You cannot get a list of listeners, but sending a payload will emit the amount of listeners that received the notification.\nSending (publishing) an event is done like so: // Any redis data let notification : RedisData = My-Notification client . publish ( notification , to : my-channel ) If you want access to the listener count: let notifiedCount = client . publish ( notification , to : my-channel ) // Future Int",
|
|
"title": "Publishing"
|
|
},
|
|
{
|
|
"location": "/redis/pub-sub/#subscribing",
|
|
"text": "To subscribe for notifications you're rendering an entire Redis Client useless in exchange for listening to events. A single client can listen to one or more channels, which is provided using a set of unique channel names. The result of subscribing is a SubscriptionStream . let notifications = client . subscribe ( to : [ some-notification-channel , other-notification-channel ]) If you try to use the client after subscribing, all operations will fail. These errors are usually emitted through the Future. This stream will receive messages asynchronously from the point of draining . This works like any other async stream. Notifications consist of the channel and payload. notifications . drain { notification in \n print ( notification . channel ) \n\n let payload = notification . payload \n\n // TODO: Process the payload }",
|
|
"title": "Subscribing"
|
|
},
|
|
{
|
|
"location": "/redis/pipeline/",
|
|
"text": "Pipelining\n\n\nPipelining is used for sending multiple commands at once. The performance advantages become apparent when sending a large number of queries. Redis' pipelining cuts down latency by reducing the RTT (Round Trip Time) between the client and server. Pipelining also reduces the amount of IO operations Redis has to perform, this increases the amount of queries per second Redis can handle. \n\n\nUse cases\n\n\nSometimes multiple commands need to be executed at once. Instead of sending those commands individually in a loop, pipelining allows the commands to be batched and sent in one request. A common scenario might be needing to set a key and increment a count, pipelining those commands would be ideal.\n\n\nEnqueuing Commands\n\n\n \nlet\n \npipeline\n \n=\n \nconnection\n.\nmakePipeline\n()\n\n \nlet\n \nresult\n \n=\n \ntry\n \npipeline\n\n \n.\nenqueue\n(\ncommand\n:\n \nSET\n,\n \narguments\n:\n \n[\nKEY\n,\n \nVALUE\n])\n\n \n.\nenqueue\n(\ncommand\n:\n \nINCR\n,\n \narguments\n:\n \n[\nCOUNT\n])\n\n \n.\nexecute\n()\n \n// Future\n[RedisData]\n\n\n\n\n\n\nNote: Commands will not be executed until execute is called.",
|
|
"title": "Pipeline"
|
|
},
|
|
{
|
|
"location": "/redis/pipeline/#pipelining",
|
|
"text": "Pipelining is used for sending multiple commands at once. The performance advantages become apparent when sending a large number of queries. Redis' pipelining cuts down latency by reducing the RTT (Round Trip Time) between the client and server. Pipelining also reduces the amount of IO operations Redis has to perform, this increases the amount of queries per second Redis can handle.",
|
|
"title": "Pipelining"
|
|
},
|
|
{
|
|
"location": "/redis/pipeline/#use-cases",
|
|
"text": "Sometimes multiple commands need to be executed at once. Instead of sending those commands individually in a loop, pipelining allows the commands to be batched and sent in one request. A common scenario might be needing to set a key and increment a count, pipelining those commands would be ideal.",
|
|
"title": "Use cases"
|
|
},
|
|
{
|
|
"location": "/redis/pipeline/#enqueuing-commands",
|
|
"text": "let pipeline = connection . makePipeline () \n let result = try pipeline \n . enqueue ( command : SET , arguments : [ KEY , VALUE ]) \n . enqueue ( command : INCR , arguments : [ COUNT ]) \n . execute () // Future [RedisData] Note: Commands will not be executed until execute is called.",
|
|
"title": "Enqueuing Commands"
|
|
},
|
|
{
|
|
"location": "/routing/getting-started/",
|
|
"text": "Getting Started with Routing\n\n\nRouting (\nvapor/routing\n) is a small framework for routing things like HTTP requests. It lets you register and lookup routes in a router using nested, dynamic path components.\n\n\nFor example, the routing package can help you route a request like the following and collect the values of the dynamic components.\n\n\n/users/:user_id/comments/:comment_id\n\n\n\n\n\nVapor\n\n\nThis package is included with Vapor and exported by default. You will have access to all \nRouting\n APIs when you import \nVapor\n.\n\n\n\n\nTip\n\n\nIf you use Vapor, most of Routing's APIs will be wrapped by more convenient methods. See \nVapor \n Routing\n for more information.\n\n\n\n\nimport\n \nVapor\n\n\n\n\n\n\nStandalone\n\n\nThe Routing package is lightweight, pure-Swift, and has very few dependencies. This means it can be used as a routing framework for any Swift project\neven one not using Vapor.\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\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 \nfrom\n:\n \n3.0.0\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nRouting\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Routing\n to access the APIs.\n\n\n\n\nWarning\n\n\nSome of this guide may contain Vapor-specific APIs, however most of it should be applicable to the Routing package in general.\nVisit the \nAPI Docs\n for Routing-specific API info.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/routing/getting-started/#getting-started-with-routing",
|
|
"text": "Routing ( vapor/routing ) is a small framework for routing things like HTTP requests. It lets you register and lookup routes in a router using nested, dynamic path components. For example, the routing package can help you route a request like the following and collect the values of the dynamic components. /users/:user_id/comments/:comment_id",
|
|
"title": "Getting Started with Routing"
|
|
},
|
|
{
|
|
"location": "/routing/getting-started/#vapor",
|
|
"text": "This package is included with Vapor and exported by default. You will have access to all Routing APIs when you import Vapor . Tip If you use Vapor, most of Routing's APIs will be wrapped by more convenient methods. See Vapor Routing for more information. import Vapor",
|
|
"title": "Vapor"
|
|
},
|
|
{
|
|
"location": "/routing/getting-started/#standalone",
|
|
"text": "The Routing package is lightweight, pure-Swift, and has very few dependencies. This means it can be used as a routing framework for any Swift project even one not using Vapor. To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . package ( url : https://github.com/vapor/routing.git , from : 3.0.0 ), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ Routing , ... ]) \n ] ) Use import Routing to access the APIs. Warning Some of this guide may contain Vapor-specific APIs, however most of it should be applicable to the Routing package in general.\nVisit the API Docs for Routing-specific API info.",
|
|
"title": "Standalone"
|
|
},
|
|
{
|
|
"location": "/routing/overview/",
|
|
"text": "Using Routing\n\n\nRouting (\nvapor/routing\n) is a small framework for routing things like HTTP requests. It lets you register and lookup routes in a router using nested, dynamic path components.\n\n\n\n\nTip\n\n\nIf you use Vapor, most of Routing's APIs will be wrapped by more convenient methods. See [Vapor \u2192 Routing] for more information.\n\n\n\n\nThis guide will show you how to register a static route and a dynamic route and how to use \nParameter\ns.\n\n\nRegister\n\n\nThe first step to routing is to register some routes. Let's take a look at how to do that with a simple router\na \nTrieRouter\nDouble\n which holds numbers. Usually you would store something like HTTP responders, but we'll keep things simple for this example.\n\n\n// Create a router that stores Doubles\n\n\nlet\n \nrouter\n \n=\n \nTrieRouter\n(\nDouble\n.\nself\n)\n\n\n\n// Register some routes and values to the router\n\n\nrouter\n.\nregister\n(\nroute\n:\n \nRoute\n(\npath\n:\n \n[\nfunny\n,\n \nmeaning_of_universe\n],\n \noutput\n:\n \n42\n))\n\n\nrouter\n.\nregister\n(\nroute\n:\n \nRoute\n(\npath\n:\n \n[\nfunny\n,\n \nleet\n],\n \noutput\n:\n \n1337\n))\n\n\nrouter\n.\nregister\n(\nroute\n:\n \nRoute\n(\npath\n:\n \n[\nmath\n,\n \npi\n],\n \noutput\n:\n \n3.14\n))\n\n\n\n// Create empty Parameters to hold dynamic params (none yet)\n\n\nvar\n \nparams\n \n=\n \nParameters\n()\n\n\n\n// Test fetching some routes\n\n\nprint\n(\nrouter\n.\nroute\n(\npath\n:\n \n[\nfun\n,\n \nmeaning_of_universe\n],\n \nparameters\n:\n \nparams\n))\n \n// 42\n\n\nprint\n(\nrouter\n.\nroute\n(\npath\n:\n \n[\nfoo\n],\n \nparameters\n:\n \nparams\n))\n \n// nil\n\n\n\n\n\n\nHere we are using \nregister(...)\n to register routes to our router, then later \nroute(...)\n to fetch them. The \nTrieRouter\n uses a trie (digital tree) internally to make finding value in the router fast.\n\n\nParameter\n\n\nLet's take a look at registering some dynamic path components. These are parts of the path that are variable and whose value should be collected for later use. You will often see this used for situations like show a webpage for a user:\n\n\n/users/:user_id\n\n\n\n\n\nHere is how you would implement that with \nTrieRouter\n. For this example, we will ignore the route output.\n\n\n// Create a route for /users/:user_id\n\n\nlet\n \nuser\n \n=\n \nRoute\n(\npath\n:\n \n[.\nconstant\n(\nusers\n),\n \n.\nparameter\n(\nuser_id\n)],\n \noutput\n:\n \n...)\n\n\n\n// Create a router and register our route\n\n\nlet\n \nrouter\n \n=\n \nTrieRouter\n(...)\n\n\nrouter\n.\nregister\n(\nroute\n:\n \nuser\n)\n\n\n\n// Create empty Parameters to hold dynamic values\n\n\nvar\n \nparams\n \n=\n \nParameters\n()\n\n\n\n// Route the path /users/42\n\n\n_\n \n=\n \nrouter\n.\nroute\n(\npath\n:\n \n[\nusers\n,\n \n42\n],\n \nparameters\n:\n \nparams\n)\n\n\n\n// The params contains our dynamic value!\n\n\nprint\n(\nparams\n)\n \n// [\nuser_id\n: \n42\n]\n\n\n\n\n\n\nNote that the String used for \n.parameter(...)\n will be the key to fetch the value from \nParameters\n.\n\n\nAPI Docs\n\n\nCheck out the \nAPI docs\n for more in-depth information about all of the available parameters and methods.",
|
|
"title": "Overview"
|
|
},
|
|
{
|
|
"location": "/routing/overview/#using-routing",
|
|
"text": "Routing ( vapor/routing ) is a small framework for routing things like HTTP requests. It lets you register and lookup routes in a router using nested, dynamic path components. Tip If you use Vapor, most of Routing's APIs will be wrapped by more convenient methods. See [Vapor \u2192 Routing] for more information. This guide will show you how to register a static route and a dynamic route and how to use Parameter s.",
|
|
"title": "Using Routing"
|
|
},
|
|
{
|
|
"location": "/routing/overview/#register",
|
|
"text": "The first step to routing is to register some routes. Let's take a look at how to do that with a simple router a TrieRouter Double which holds numbers. Usually you would store something like HTTP responders, but we'll keep things simple for this example. // Create a router that stores Doubles let router = TrieRouter ( Double . self ) // Register some routes and values to the router router . register ( route : Route ( path : [ funny , meaning_of_universe ], output : 42 )) router . register ( route : Route ( path : [ funny , leet ], output : 1337 )) router . register ( route : Route ( path : [ math , pi ], output : 3.14 )) // Create empty Parameters to hold dynamic params (none yet) var params = Parameters () // Test fetching some routes print ( router . route ( path : [ fun , meaning_of_universe ], parameters : params )) // 42 print ( router . route ( path : [ foo ], parameters : params )) // nil Here we are using register(...) to register routes to our router, then later route(...) to fetch them. The TrieRouter uses a trie (digital tree) internally to make finding value in the router fast.",
|
|
"title": "Register"
|
|
},
|
|
{
|
|
"location": "/routing/overview/#parameter",
|
|
"text": "Let's take a look at registering some dynamic path components. These are parts of the path that are variable and whose value should be collected for later use. You will often see this used for situations like show a webpage for a user: /users/:user_id Here is how you would implement that with TrieRouter . For this example, we will ignore the route output. // Create a route for /users/:user_id let user = Route ( path : [. constant ( users ), . parameter ( user_id )], output : ...) // Create a router and register our route let router = TrieRouter (...) router . register ( route : user ) // Create empty Parameters to hold dynamic values var params = Parameters () // Route the path /users/42 _ = router . route ( path : [ users , 42 ], parameters : params ) // The params contains our dynamic value! print ( params ) // [ user_id : 42 ] Note that the String used for .parameter(...) will be the key to fetch the value from Parameters .",
|
|
"title": "Parameter"
|
|
},
|
|
{
|
|
"location": "/routing/overview/#api-docs",
|
|
"text": "Check out the API docs for more in-depth information about all of the available parameters and methods.",
|
|
"title": "API Docs"
|
|
},
|
|
{
|
|
"location": "/service/getting-started/",
|
|
"text": "Getting Started with Service\n\n\nService (\nvapor/service\n) is a dependency injection (inversion of control) framework. It allows you to register, configure, and create your application's dependencies in a maintainable way.\n\n\n/// register a service during boot\n\n\nservices\n.\nregister\n(\nPrintLogger\n.\nself\n,\n \nas\n:\n \nLogger\n.\nself\n)\n\n\n\n/// you can then create that service later\n\n\nlet\n \nlogger\n \n=\n \ntry\n \nsomeContainer\n.\nmake\n(\nLogger\n.\nself\n)\n\n\nprint\n(\nlogger\n \nis\n \nPrintLogger\n)\n \n// true\n\n\n\n\n\n\nYou can read more about \ndependency injection\n on Wikipedia. Also be sure to check out the \nGetting Started \n Services\n guide.\n\n\nVapor\n\n\nThis package is included with Vapor and exported by default. You will have access to all \nService\n APIs when you import \nVapor\n.\n\n\nimport\n \nVapor\n\n\n\n\n\n\nStandalone\n\n\nThe Service package is lightweight, pure-Swift, and has very few dependencies. This means it can be used as a dependency injection framework for any Swift project\neven one not using Vapor.\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\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/service.git\n,\n \nfrom\n:\n \n1.0.0\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nService\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Service\n to access the APIs.\n\n\n\n\nWarning\n\n\nSome of this guide may contain Vapor-specific APIs, however most of it should be applicable to the Services package in general.\nVisit the \nAPI Docs\n for Service-specific API info.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/service/getting-started/#getting-started-with-service",
|
|
"text": "Service ( vapor/service ) is a dependency injection (inversion of control) framework. It allows you to register, configure, and create your application's dependencies in a maintainable way. /// register a service during boot services . register ( PrintLogger . self , as : Logger . self ) /// you can then create that service later let logger = try someContainer . make ( Logger . self ) print ( logger is PrintLogger ) // true You can read more about dependency injection on Wikipedia. Also be sure to check out the Getting Started Services guide.",
|
|
"title": "Getting Started with Service"
|
|
},
|
|
{
|
|
"location": "/service/getting-started/#vapor",
|
|
"text": "This package is included with Vapor and exported by default. You will have access to all Service APIs when you import Vapor . import Vapor",
|
|
"title": "Vapor"
|
|
},
|
|
{
|
|
"location": "/service/getting-started/#standalone",
|
|
"text": "The Service package is lightweight, pure-Swift, and has very few dependencies. This means it can be used as a dependency injection framework for any Swift project even one not using Vapor. To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . package ( url : https://github.com/vapor/service.git , from : 1.0.0 ), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ Service , ... ]) \n ] ) Use import Service to access the APIs. Warning Some of this guide may contain Vapor-specific APIs, however most of it should be applicable to the Services package in general.\nVisit the API Docs for Service-specific API info.",
|
|
"title": "Standalone"
|
|
},
|
|
{
|
|
"location": "/service/services/",
|
|
"text": "Using Services\n\n\nThis guide will show you how to register, configure, and create your own service. In this example we will be assuming two different \nLogger\n implementations.\n\n\n\n\nPrintLogger\n: Prints logs.\n\n\nFileLogger\n: Saves logs to a file. Already conforms to \nServiceType\n.\n\n\n\n\nRegister\n\n\nLet's take a look at how we can register our \nPrintLogger\n. First you must conform your type to \nService\n. The easiest way to do this is simply adding the conformance in an extension.\n\n\nextension\n \nPrintLogger\n:\n \nService\n \n{\n \n}\n\n\n\n\n\n\nIt's an empty protocol so there should be no missing requirements.\n\n\nFactory\n\n\nNow the service can be registered to the \nServices\n struct. This is usually done in \nconfigure.swift\n.\n\n\nservices\n.\nregister\n(\nLogger\n.\nself\n)\n \n{\n \ncontainer\n \nin\n\n \nreturn\n \nPrintLogger\n()\n\n\n}\n\n\n\n\n\n\nBy registering the \nPrintLogger\n using a factory (closure) method, we allow the \nContainer\n to dynamically create the service once it is needed. Any \nSubContainer\ns created later can call this method again to create their own \nPrintLogger\ns.\n\n\nService Type\n\n\nTo make registering a service easier, you can conform it to \nServiceType\n.\n\n\nextension\n \nPrintLogger\n:\n \nServiceType\n \n{\n\n \n/// See `ServiceType`.\n\n \nstatic\n \nvar\n \nserviceSupports\n:\n \n[\nAny\n.\nType\n]\n \n{\n\n \nreturn\n \n[\nLogger\n.\nself\n]\n\n \n}\n\n\n \n/// See `ServiceType`.\n\n \nstatic\n \nfunc\n \nmakeService\n(\nfor\n \nworker\n:\n \nContainer\n)\n \nthrows\n \n-\n \nPrintLogger\n \n{\n\n \nreturn\n \nPrintLogger\n()\n\n \n}\n\n\n}\n\n\n\n\n\n\nServices conforming to \nServiceType\n can be registered using just the type name. This will automatically conform to \nService\n as well.\n\n\nservices\n.\nregister\n(\nPrintLogger\n.\nself\n)\n\n\n\n\n\n\nInstance\n\n\nYou can also register pre-initialized instances to \nServices\n.\n\n\nservices\n.\nregister\n(\nPrintLogger\n(),\n \nas\n:\n \nLogger\n.\nself\n)\n\n\n\n\n\n\n\n\nWarning\n\n\nIf using reference types (\nclass\n) this method will share the \nsame\n object between all \nContainer\ns and \nSubContainer\ns.\nBe careful to protect against race conditions.\n\n\n\n\nConfigure\n\n\nIf more than one service is registered for a given interface, we will need to choose which service is used.\n\n\nservices\n.\nregister\n(\nPrintLogger\n.\nself\n)\n\n\nservices\n.\nregister\n(\nFileLogger\n.\nself\n)\n\n\n\n\n\n\nAssuming the above services are registered, we can use service \nConfig\n to pick which one we want.\n\n\nswitch\n \nenv\n \n{\n\n\ncase\n \n.\nproduction\n:\n \nconfig\n.\nprefer\n(\nFileLogger\n.\nself\n,\n \nfor\n:\n \nLogger\n.\nself\n)\n\n\ndefault\n:\n \nconfig\n.\nprefer\n(\nPrintLogger\n.\nself\n,\n \nfor\n:\n \nLogger\n.\nself\n)\n\n\n}\n\n\n\n\n\n\nHere we are using the \nEnvironment\n to dynamically prefer a service. This is usually done in \nconfigure.swift\n.\n\n\n\n\nNote\n\n\nYou can also dynamically \nregister\n services based on environment instead of using service config. \nHowever, service config is required for choosing services that come from the framework or a provider.\n\n\n\n\nCreate\n\n\nAfter you have registered your services, you can use a \nContainer\n to create them.\n\n\nlet\n \nlogger\n \n=\n \ntry\n \nsomeContainer\n.\nmake\n(\nLogger\n.\nself\n)\n\n\nlogger\n.\nlog\n(\nHello, world!\n)\n\n\n\n// PrintLogger or FileLogger depending on the container\ns environment\n\n\nprint\n(\ntype\n(\nof\n:\n \nlogger\n))\n \n\n\n\n\n\n\n\nTip\n\n\nUsually the framework will create any required containers for you. You can use \nBasicContainer\n if you want to create one for testing.",
|
|
"title": "Services"
|
|
},
|
|
{
|
|
"location": "/service/services/#using-services",
|
|
"text": "This guide will show you how to register, configure, and create your own service. In this example we will be assuming two different Logger implementations. PrintLogger : Prints logs. FileLogger : Saves logs to a file. Already conforms to ServiceType .",
|
|
"title": "Using Services"
|
|
},
|
|
{
|
|
"location": "/service/services/#register",
|
|
"text": "Let's take a look at how we can register our PrintLogger . First you must conform your type to Service . The easiest way to do this is simply adding the conformance in an extension. extension PrintLogger : Service { } It's an empty protocol so there should be no missing requirements.",
|
|
"title": "Register"
|
|
},
|
|
{
|
|
"location": "/service/services/#factory",
|
|
"text": "Now the service can be registered to the Services struct. This is usually done in configure.swift . services . register ( Logger . self ) { container in \n return PrintLogger () } By registering the PrintLogger using a factory (closure) method, we allow the Container to dynamically create the service once it is needed. Any SubContainer s created later can call this method again to create their own PrintLogger s.",
|
|
"title": "Factory"
|
|
},
|
|
{
|
|
"location": "/service/services/#service-type",
|
|
"text": "To make registering a service easier, you can conform it to ServiceType . extension PrintLogger : ServiceType { \n /// See `ServiceType`. \n static var serviceSupports : [ Any . Type ] { \n return [ Logger . self ] \n } \n\n /// See `ServiceType`. \n static func makeService ( for worker : Container ) throws - PrintLogger { \n return PrintLogger () \n } } Services conforming to ServiceType can be registered using just the type name. This will automatically conform to Service as well. services . register ( PrintLogger . self )",
|
|
"title": "Service Type"
|
|
},
|
|
{
|
|
"location": "/service/services/#instance",
|
|
"text": "You can also register pre-initialized instances to Services . services . register ( PrintLogger (), as : Logger . self ) Warning If using reference types ( class ) this method will share the same object between all Container s and SubContainer s.\nBe careful to protect against race conditions.",
|
|
"title": "Instance"
|
|
},
|
|
{
|
|
"location": "/service/services/#configure",
|
|
"text": "If more than one service is registered for a given interface, we will need to choose which service is used. services . register ( PrintLogger . self ) services . register ( FileLogger . self ) Assuming the above services are registered, we can use service Config to pick which one we want. switch env { case . production : config . prefer ( FileLogger . self , for : Logger . self ) default : config . prefer ( PrintLogger . self , for : Logger . self ) } Here we are using the Environment to dynamically prefer a service. This is usually done in configure.swift . Note You can also dynamically register services based on environment instead of using service config. \nHowever, service config is required for choosing services that come from the framework or a provider.",
|
|
"title": "Configure"
|
|
},
|
|
{
|
|
"location": "/service/services/#create",
|
|
"text": "After you have registered your services, you can use a Container to create them. let logger = try someContainer . make ( Logger . self ) logger . log ( Hello, world! ) // PrintLogger or FileLogger depending on the container s environment print ( type ( of : logger )) Tip Usually the framework will create any required containers for you. You can use BasicContainer if you want to create one for testing.",
|
|
"title": "Create"
|
|
},
|
|
{
|
|
"location": "/service/provider/",
|
|
"text": "Using Providers\n\n\nThe \nProvider\n protocol make it easy to integrate external services into your application. All of Vapor's official packages, like \nFluent\n, use the provider system to expose their services. \n\n\nProviders can:\n\n\n\n\nRegister services to your \nServices\n struct.\n\n\nHook into your \nContainer\n's lifecycle.\n\n\n\n\nRegister\n\n\nOnce you have added a Service-exposing \nSPM dependency\n to your project, adding the provider is easy.\n\n\nimport\n \nFoo\n\n\n\ntry\n \nservices\n.\nregister\n(\nFooProvider\n())\n\n\n\n\n\n\nThis is usually done in \nconfigure.swift\n. \n\n\n\n\nNote\n\n\nYou can search GitHub for the \nvapor-service\n tag for a list of packages that expose services using this method.\n\n\n\n\nCreate\n\n\nCreating a custom provider can be a great way to organize your code. You will also want to create a provider if you are working on a third-party package for Vapor.\n\n\nHere is what a simple provider would look like for the \nLogger\n examples from the \nServices\n section.\n\n\npublic\n \nfinal\n \nclass\n \nLoggerProvider\n:\n \nProvider\n \n{\n\n \n/// See `Provider`.\n\n \npublic\n \nfunc\n \nregister\n(\n_\n \nservices\n:\n \ninout\n \nServices\n)\n \nthrows\n \n{\n\n \nservices\n.\nregister\n(\nPrintLogger\n.\nself\n)\n\n \nservices\n.\nregister\n(\nFileLogger\n.\nself\n)\n\n \n}\n\n\n \n/// See `Provider`.\n\n \npublic\n \nfunc\n \ndidBoot\n(\n_\n \ncontainer\n:\n \nContainer\n)\n \nthrows\n \n-\n \nFuture\nVoid\n \n{\n\n \nlet\n \nlogger\n \n=\n \ntry\n \ncontainer\n.\nmake\n(\nLogger\n.\nself\n)\n\n \nlogger\n.\nlog\n(\nHello from LoggerProvider!\n)\n\n \nreturn\n \n.\ndone\n(\non\n:\n \ncontainer\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nNow when someone registers the \nLoggerProvider\n to their \nServices\n struct, it will automatically register the print and file loggers. When the container boots, the success message will be printed to verify the provider was added.\n\n\nSee the \nProvider\n protocol's API docs for more information.",
|
|
"title": "Provider"
|
|
},
|
|
{
|
|
"location": "/service/provider/#using-providers",
|
|
"text": "The Provider protocol make it easy to integrate external services into your application. All of Vapor's official packages, like Fluent , use the provider system to expose their services. Providers can: Register services to your Services struct. Hook into your Container 's lifecycle.",
|
|
"title": "Using Providers"
|
|
},
|
|
{
|
|
"location": "/service/provider/#register",
|
|
"text": "Once you have added a Service-exposing SPM dependency to your project, adding the provider is easy. import Foo try services . register ( FooProvider ()) This is usually done in configure.swift . Note You can search GitHub for the vapor-service tag for a list of packages that expose services using this method.",
|
|
"title": "Register"
|
|
},
|
|
{
|
|
"location": "/service/provider/#create",
|
|
"text": "Creating a custom provider can be a great way to organize your code. You will also want to create a provider if you are working on a third-party package for Vapor. Here is what a simple provider would look like for the Logger examples from the Services section. public final class LoggerProvider : Provider { \n /// See `Provider`. \n public func register ( _ services : inout Services ) throws { \n services . register ( PrintLogger . self ) \n services . register ( FileLogger . self ) \n } \n\n /// See `Provider`. \n public func didBoot ( _ container : Container ) throws - Future Void { \n let logger = try container . make ( Logger . self ) \n logger . log ( Hello from LoggerProvider! ) \n return . done ( on : container ) \n } } Now when someone registers the LoggerProvider to their Services struct, it will automatically register the print and file loggers. When the container boots, the success message will be printed to verify the provider was added. See the Provider protocol's API docs for more information.",
|
|
"title": "Create"
|
|
},
|
|
{
|
|
"location": "/sql/getting-started/",
|
|
"text": "Getting Started with SQL\n\n\nSQL (\nvapor/sql\n) is a library for building and serializing SQL queries in Swift. It has an extensible, protocol-based design and supports DQL, DML, and DDL.\n\n\n\n\nTip\n\n\nIf you use Fluent, you will usually not need to build SQL queries manually.\n\n\n\n\nPackage\n\n\nThe SQL package is lightweight, pure Swift, and has no dependencies. This means it can be used as a SQL serialization framework any Swift project\u2014even one not using Vapor.\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\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/sql.git\n,\n \nfrom\n:\n \n1.0.0\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nSQL\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport SQL\n to access the APIs.\n\n\nThe rest of this guide will give you an overview of what is available in the SQL package. As always, feel free to visit the \nAPI docs\n for more in-depth information.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/sql/getting-started/#getting-started-with-sql",
|
|
"text": "SQL ( vapor/sql ) is a library for building and serializing SQL queries in Swift. It has an extensible, protocol-based design and supports DQL, DML, and DDL. Tip If you use Fluent, you will usually not need to build SQL queries manually.",
|
|
"title": "Getting Started with SQL"
|
|
},
|
|
{
|
|
"location": "/sql/getting-started/#package",
|
|
"text": "The SQL package is lightweight, pure Swift, and has no dependencies. This means it can be used as a SQL serialization framework any Swift project\u2014even one not using Vapor. To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . package ( url : https://github.com/vapor/sql.git , from : 1.0.0 ), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ SQL , ... ]) \n ] ) Use import SQL to access the APIs. The rest of this guide will give you an overview of what is available in the SQL package. As always, feel free to visit the API docs for more in-depth information.",
|
|
"title": "Package"
|
|
},
|
|
{
|
|
"location": "/sql/overview/",
|
|
"text": "Using SQL\n\n\nThe SQL library helps you build and serialize SQL queries in Swift. It has an extensible, protocol-based design and supports DQL, DML, and DDL:\n\n\n\n\nDQL\n: Data Query Language (\nSELECT\n)\n\n\nDML\n: Data Manpulation Language (\nINSERT\n, \nDELETE\n, etc)\n\n\nDDL\n: Data Definition Language (\nCREATE\n, \nALTER\n, etc)\n\n\n\n\nThis library's goal is to help you build SQL queries in a type-safe, consistent way. It can be used by ORMs to serialize their data types into SQL. It can also be used to generate SQL in a more Swifty way. The rest of this guide will give you an overview into using the SQL library manually.\n\n\nData Query\n\n\nDQL (data query language) is used to fetch data from one or more tables. This is done via the \nSELECT\n statement. Let's take a look how we would serialize the following SQL query.\n\n\nSELECT\n \n*\n \nFROM\n \nusers\n \nWHERE\n \nname\n \n=\n \n?\n\n\n\n\n\n\nThis query selects all rows from the table \nusers\n where the name is equal to a parameterized value. You can serialize literal values into data queries as well, but parameterization is highly recommended.\n\n\nLet's build this query in Swift.\n\n\n// Create a new data query for the table.\n\n\nvar\n \nusers\n \n=\n \nDataQuery\n(\ntable\n:\n \nusers\n)\n\n\n\n// Create the \nname = ?\n predicate.\n\n\nlet\n \nname\n \n=\n \nDataPredicate\n(\ncolumn\n:\n \nname\n,\n \ncomparison\n:\n \n.\nequal\n,\n \nvalue\n:\n \n.\nplaceholder\n)\n\n\n\n// Add the predicate as a single item (not group) to the query.\n\n\nusers\n.\npredicates\n.\nappend\n(.\npredicate\n(\nname\n))\n\n\n\n// Serialize the query.\n\n\nlet\n \nsql\n \n=\n \nGeneralSQLSerializer\n.\nshared\n.\nserialize\n(\nquery\n:\n \nusers\n)\n\n\nprint\n(\nsql\n)\n \n// \nSELECT * FROM `users` WHERE (`name` = ?)\n\n\n\n\n\n\nHere we are using the shared \nGeneralSQLSerializer\n to serialize the query. You can also implement \ncustom serializers\n.\n\n\nData Manipulation\n\n\nDML (data manipulation language) is used to mutate data in a table. This is done via statements like \nINSERT\n, \nUPDATE\n, and \nDELETE\n. Let's take a look how we would serialize the following SQL query.\n\n\nINSERT\n \nINTO\n \nusers\n \n(\nname\n)\n \nVALUES\n \n(\n?\n)\n\n\n\n\n\n\nThis query inserts a new row into the table \nusers\n where the name is equal to a parameterized value. You can serialize literal values into data manipulation queries as well, but parameterization is highly recommended.\n\n\nLet's build this query in Swift.\n\n\n// Create a new data manipulation query for the table.\n\n\nvar\n \nusers\n \n=\n \nDataManipulationQuery\n(\nstatement\n:\n \n.\ninsert\n,\n \ntable\n:\n \nusers\n)\n\n\n\n// Create the column + value.\n\n\nlet\n \nname\n \n=\n \nDataManipulationColumn\n(\ncolumn\n:\n \nname\n,\n \nvalue\n:\n \n.\nplaceholder\n)\n\n\n\n// Add the column + value to the query.\n\n\nusers\n.\ncolumns\n.\nappend\n(\nname\n)\n\n\n\n// Serialize the query.\n\n\nlet\n \nsql\n \n=\n \nGeneralSQLSerializer\n.\nshared\n.\nserialize\n(\nquery\n:\n \nuser\n)\n\n\nprint\n(\nsql\n)\n \n// \nINSERT INTO `users` (`name`) VALUES (?)\n\n\n\n\n\n\nThat's all it takes to generate an \nINSERT\n query. Let's take a look at how this query would serialize if we use the \n.update\n statement instead.\n\n\n// Change the statement type\n\n\nusers\n.\nstatement\n \n=\n \n.\nupdate\n\n\n\n// Serialize the query.\n\n\nlet\n \nsql\n \n=\n \nGeneralSQLSerializer\n.\nshared\n.\nserialize\n(\nquery\n:\n \nuser\n)\n\n\nprint\n(\nsql\n)\n \n// \nUPDATE `users` SET `name` = ?\n\n\n\n\n\n\nYou can see that SQL has generated an equivalent \nUPDATE\n query with the appropriate syntax.\n\n\nData Definition\n\n\nDDL (data definition language) is used to create, update, and delete schemas in the database. This is done via statements like \nCREATE TABLE\n, \nDROP TABLE\n, etc. Let's take a look at how we would serialize the following SQL query.\n\n\nCREATE\n \nTABLE\n \nusers\n \n(\nid\n \nINTEGER\n \nPRIMARY\n \nKEY\n,\n \nname\n \nTEXT\n)\n\n\n\n\n\n\nThis example is using SQLite-like syntax, but that is not required. Let's generate this query in Swift.\n\n\n// Create a new data definition query for the table.\n\n\nvar\n \nusers\n \n=\n \nDataDefinitionQuery\n(\nstatement\n:\n \n.\ncreate\n,\n \ntable\n:\n \nusers\n)\n\n\n\n// Create and append the id column.\n\n\nlet\n \nid\n \n=\n \nDataDefinitionColumn\n(\nname\n:\n \nid\n,\n \ndataType\n:\n \nINTEGER\n,\n \nattributes\n:\n \n[\nPRIMARY KEY\n])\n\n\nusers\n.\naddColumns\n.\nappend\n(\nid\n)\n\n\n\n// Create and append the name column.\n\n\nlet\n \nname\n \n=\n \nDataDefinitionColumn\n(\nname\n:\n \nname\n,\n \ndataType\n:\n \nTEXT\n)\n\n\nusers\n.\naddColumns\n.\nappend\n(\nname\n)\n\n\n\n// Serialize the query.\n\n\nlet\n \nsql\n \n=\n \nGeneralSQLSerializer\n.\nshared\n.\nserialize\n(\nquery\n:\n \nuser\n)\n\n\nprint\n(\nsql\n)\n \n// \nCREATE TABLE `users` (`id` INTEGER PRIMARY KEY, `name` TEXT)\n\n\n\n\n\n\nThat's all it takes to generate a \nCREATE\n query.\n\n\nSerializer\n\n\nThe default \nGeneralSQLSerializer\n that comes with this library generates a fairly \"standard\" SQL syntax. However, each flavor of SQL (SQLite, MySQL, PostgreSQL, etc) all have specific rules for their syntax. \n\n\nTo deal with this, all serializers are backed by the \nSQLSerializer\n protocol. All serialization methods are defined on this protocol and come with a default implementation. Custom serializers can conform to this protocol and implement only the methods they need to customize. \n\n\nLet's take a look at how we would implement a \nPostgreSQLSerializer\n that uses \n$x\n placeholders instead of \n?\n.\n\n\n/// Postgres-flavor SQL serializer.\n\n\nfinal\n \nclass\n \nPostgreSQLSerializer\n:\n \nSQLSerializer\n \n{\n\n \n/// Keeps track of the current placeholder count.\n\n \nvar\n \ncount\n:\n \nInt\n\n\n \n/// Creates a new `PostgreSQLSerializer`.\n\n \ninit\n()\n \n{\n\n \nself\n.\ncount\n \n=\n \n1\n\n \n}\n\n\n \n/// See `SQLSerializer`.\n\n \nfunc\n \nmakePlaceholder\n()\n \n-\n \nString\n \n{\n\n \ndefer\n \n{\n \ncount\n \n+=\n \n1\n \n}\n\n \nreturn\n \n$\n\\(\ncount\n)\n\n \n}\n\n\n}\n\n\n\n\n\n\nHere we've implemented \nPostgreSQLSerializer\n and overriden one method from \nSQLSerializer\n, \nmakePlaceholder()\n.\n\n\nNow let's use this serializer to serialize the \ndata query\n from a previous example.\n\n\n// Data query from previous example\n\n\nlet\n \nusers\n:\n \nDataQuery\n \n=\n \n...\n \n\n\n// Serialize the query.\n\n\nlet\n \nsql\n \n=\n \nPostgreSQLSerializer\n().\nserialize\n(\nquery\n:\n \nusers\n)\n\n\nprint\n(\nsql\n)\n \n// \nSELECT * FROM `users` WHERE (`name` = $1)\n\n\n\n\n\n\nThat's it, congratulations on implementing a custom serializer.\n\n\nAPI Docs\n\n\nCheck out the \nAPI docs\n for more in-depth information about SQL's APIs.",
|
|
"title": "Overview"
|
|
},
|
|
{
|
|
"location": "/sql/overview/#using-sql",
|
|
"text": "The SQL library helps you build and serialize SQL queries in Swift. It has an extensible, protocol-based design and supports DQL, DML, and DDL: DQL : Data Query Language ( SELECT ) DML : Data Manpulation Language ( INSERT , DELETE , etc) DDL : Data Definition Language ( CREATE , ALTER , etc) This library's goal is to help you build SQL queries in a type-safe, consistent way. It can be used by ORMs to serialize their data types into SQL. It can also be used to generate SQL in a more Swifty way. The rest of this guide will give you an overview into using the SQL library manually.",
|
|
"title": "Using SQL"
|
|
},
|
|
{
|
|
"location": "/sql/overview/#data-query",
|
|
"text": "DQL (data query language) is used to fetch data from one or more tables. This is done via the SELECT statement. Let's take a look how we would serialize the following SQL query. SELECT * FROM users WHERE name = ? This query selects all rows from the table users where the name is equal to a parameterized value. You can serialize literal values into data queries as well, but parameterization is highly recommended. Let's build this query in Swift. // Create a new data query for the table. var users = DataQuery ( table : users ) // Create the name = ? predicate. let name = DataPredicate ( column : name , comparison : . equal , value : . placeholder ) // Add the predicate as a single item (not group) to the query. users . predicates . append (. predicate ( name )) // Serialize the query. let sql = GeneralSQLSerializer . shared . serialize ( query : users ) print ( sql ) // SELECT * FROM `users` WHERE (`name` = ?) Here we are using the shared GeneralSQLSerializer to serialize the query. You can also implement custom serializers .",
|
|
"title": "Data Query"
|
|
},
|
|
{
|
|
"location": "/sql/overview/#data-manipulation",
|
|
"text": "DML (data manipulation language) is used to mutate data in a table. This is done via statements like INSERT , UPDATE , and DELETE . Let's take a look how we would serialize the following SQL query. INSERT INTO users ( name ) VALUES ( ? ) This query inserts a new row into the table users where the name is equal to a parameterized value. You can serialize literal values into data manipulation queries as well, but parameterization is highly recommended. Let's build this query in Swift. // Create a new data manipulation query for the table. var users = DataManipulationQuery ( statement : . insert , table : users ) // Create the column + value. let name = DataManipulationColumn ( column : name , value : . placeholder ) // Add the column + value to the query. users . columns . append ( name ) // Serialize the query. let sql = GeneralSQLSerializer . shared . serialize ( query : user ) print ( sql ) // INSERT INTO `users` (`name`) VALUES (?) That's all it takes to generate an INSERT query. Let's take a look at how this query would serialize if we use the .update statement instead. // Change the statement type users . statement = . update // Serialize the query. let sql = GeneralSQLSerializer . shared . serialize ( query : user ) print ( sql ) // UPDATE `users` SET `name` = ? You can see that SQL has generated an equivalent UPDATE query with the appropriate syntax.",
|
|
"title": "Data Manipulation"
|
|
},
|
|
{
|
|
"location": "/sql/overview/#data-definition",
|
|
"text": "DDL (data definition language) is used to create, update, and delete schemas in the database. This is done via statements like CREATE TABLE , DROP TABLE , etc. Let's take a look at how we would serialize the following SQL query. CREATE TABLE users ( id INTEGER PRIMARY KEY , name TEXT ) This example is using SQLite-like syntax, but that is not required. Let's generate this query in Swift. // Create a new data definition query for the table. var users = DataDefinitionQuery ( statement : . create , table : users ) // Create and append the id column. let id = DataDefinitionColumn ( name : id , dataType : INTEGER , attributes : [ PRIMARY KEY ]) users . addColumns . append ( id ) // Create and append the name column. let name = DataDefinitionColumn ( name : name , dataType : TEXT ) users . addColumns . append ( name ) // Serialize the query. let sql = GeneralSQLSerializer . shared . serialize ( query : user ) print ( sql ) // CREATE TABLE `users` (`id` INTEGER PRIMARY KEY, `name` TEXT) That's all it takes to generate a CREATE query.",
|
|
"title": "Data Definition"
|
|
},
|
|
{
|
|
"location": "/sql/overview/#serializer",
|
|
"text": "The default GeneralSQLSerializer that comes with this library generates a fairly \"standard\" SQL syntax. However, each flavor of SQL (SQLite, MySQL, PostgreSQL, etc) all have specific rules for their syntax. To deal with this, all serializers are backed by the SQLSerializer protocol. All serialization methods are defined on this protocol and come with a default implementation. Custom serializers can conform to this protocol and implement only the methods they need to customize. Let's take a look at how we would implement a PostgreSQLSerializer that uses $x placeholders instead of ? . /// Postgres-flavor SQL serializer. final class PostgreSQLSerializer : SQLSerializer { \n /// Keeps track of the current placeholder count. \n var count : Int \n\n /// Creates a new `PostgreSQLSerializer`. \n init () { \n self . count = 1 \n } \n\n /// See `SQLSerializer`. \n func makePlaceholder () - String { \n defer { count += 1 } \n return $ \\( count ) \n } } Here we've implemented PostgreSQLSerializer and overriden one method from SQLSerializer , makePlaceholder() . Now let's use this serializer to serialize the data query from a previous example. // Data query from previous example let users : DataQuery = ... // Serialize the query. let sql = PostgreSQLSerializer (). serialize ( query : users ) print ( sql ) // SELECT * FROM `users` WHERE (`name` = $1) That's it, congratulations on implementing a custom serializer.",
|
|
"title": "Serializer"
|
|
},
|
|
{
|
|
"location": "/sql/overview/#api-docs",
|
|
"text": "Check out the API docs for more in-depth information about SQL's APIs.",
|
|
"title": "API Docs"
|
|
},
|
|
{
|
|
"location": "/sqlite/getting-started/",
|
|
"text": "Warning\n\n\nSQLite 3.0 is still in beta. Some documentation may be missing or out of date.\n\n\n\n\nGetting Started with SQLite\n\n\nSQLite\n is a open-source, embedded database. Its simplistic nature makes it a great candiate for prototyping and testing.\n\n\nYou can use SQLite with Vapor (or any server-side Swift framework) by either:\n\n\n\n\nUsing \nFluent SQLite\n ORM.\n\n\nUse just \nSQLite core\n.\n\n\n\n\nWe recommend using the ORM since it does a lot of the hard work for you. Check out the respective guides to learn more.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/sqlite/getting-started/#getting-started-with-sqlite",
|
|
"text": "SQLite is a open-source, embedded database. Its simplistic nature makes it a great candiate for prototyping and testing. You can use SQLite with Vapor (or any server-side Swift framework) by either: Using Fluent SQLite ORM. Use just SQLite core . We recommend using the ORM since it does a lot of the hard work for you. Check out the respective guides to learn more.",
|
|
"title": "Getting Started with SQLite"
|
|
},
|
|
{
|
|
"location": "/sqlite/fluent/",
|
|
"text": "Fluent SQLite\n\n\nFluent SQLite (\nvapor/fluent-sqlite\n) is a type-safe, fast, and easy-to-use ORM for SQLite built on top of \nFluent\n.\n\n\n\n\nSeealso\n\n\nThe Fluent SQLite package is built on top of \nFluent\n and the pure Swift, NIO-based \nSQLite core\n. You should refer to their guides for more information about subjects not covered here.\n\n\n\n\nGetting Started\n\n\nThis section will show you how to add Fluent SQLite to your project and create your first \nSQLiteModel\n.\n\n\nPackage\n\n\nThe first step to using Fluent SQLite is adding it as a dependency to your project in your SPM package manifest file.\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\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/// Any other dependencies ...\n\n\n \n// \ud83d\udd8b\ud83d\udc2c Swift ORM (queries, models, relations, etc) built on SQLite.\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/fluent-sqlite.git\n,\n \nfrom\n:\n \n3.0.0-rc\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nApp\n,\n \ndependencies\n:\n \n[\nFluentSQLite\n,\n \n...]),\n\n \n.\ntarget\n(\nname\n:\n \nRun\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n.\ntestTarget\n(\nname\n:\n \nAppTests\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n]\n\n\n)\n\n\n\n\n\n\nDon't forget to add the module as a dependency in the \ntargets\n array. Once you have added the dependency, regenerate your Xcode project with the following command:\n\n\nvapor xcode\n\n\n\n\n\nModel\n\n\nNow let's create our first \nSQLiteModel\n. Models represent tables in your SQLite database and they are the primary method of interacting with your data. \n\n\n/// A simple user.\n\n\nfinal\n \nclass\n \nUser\n:\n \nSQLiteModel\n \n{\n\n \n/// The unique identifier for this user.\n\n \nvar\n \nid\n:\n \nInt\n?\n\n\n \n/// The user\ns full name.\n\n \nvar\n \nname\n:\n \nString\n\n\n \n/// The user\ns current age in years.\n\n \nvar\n \nage\n:\n \nInt\n\n\n \n/// Creates a new user.\n\n \ninit\n(\nid\n:\n \nInt\n?\n \n=\n \nnil\n,\n \nname\n:\n \nString\n,\n \nage\n:\n \nInt\n)\n \n{\n\n \nself\n.\nid\n \n=\n \nid\n\n \nself\n.\nname\n \n=\n \nname\n\n \nself\n.\nage\n \n=\n \nage\n\n \n}\n\n\n}\n\n\n\n\n\n\nThe example above shows a \nSQLiteModel\n for a simple model representing a user. You can make both \nstruct\ns and \nclass\nes a model. You can even conform types that come from external modules. The only requirement is that these types conform to \nCodable\n, which must be declared on the base type for synthesized (automatic) conformance.\n\n\nStandard practice with SQLite databases is using an auto-generated \nINTEGER\n for creating and storing unique identifiers in the \nid\n column. It's also possible to use \nUUID\ns or even \nString\ns for your identifiers. There are convenience protocol for that. \n\n\n\n\n\n\n\n\nprotocol\n\n\ntype\n\n\nkey\n\n\n\n\n\n\n\n\n\n\nSQLiteModel\n\n\nInt\n\n\nid\n\n\n\n\n\n\nSQLiteUUIDModel\n\n\nUUID\n\n\nid\n\n\n\n\n\n\nSQLiteStringModel\n\n\nString\n\n\nid\n\n\n\n\n\n\n\n\n\n\nSeealso\n\n\nTake a look at \nFluent \n Model\n for more information on creating models with custom ID types and keys.\n\n\n\n\nMigration\n\n\nAll of your models (with some rare exceptions) should have a corresponding table\nor \nschema\nin your database. You can use a \nFluent \n Migration\n to automatically generate this schema in a testable, maintainable way. Fluent makes it easy to automatically generate a migration for your model\n\n\n\n\nTip\n\n\nIf you are creating models to represent an existing table or database, you can skip this step.\n\n\n\n\n/// Allows `User` to be used as a migration.\n\n\nextension\n \nUser\n:\n \nMigration\n \n{\n \n}\n\n\n\n\n\n\nThat's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it.\n\n\nTake a look at \nFluent \n Migration\n if you are interested in customizing this migration.\n\n\nConfigure\n\n\nThe final step is to configure your database. At a minimum, this requires adding two things to your \nconfigure.swift\n file.\n\n\n\n\nFluentSQLiteProvider\n\n\nMigrationConfig\n\n\n\n\nLet's take a look.\n\n\nimport\n \nFluentSQLite\n\n\n\n/// ...\n\n\n\n/// Register providers first\n\n\ntry\n \nservices\n.\nregister\n(\nFluentSQLiteProvider\n())\n\n\n\n/// Configure migrations\n\n\nvar\n \nmigrations\n \n=\n \nMigrationConfig\n()\n\n\nmigrations\n.\nadd\n(\nmodel\n:\n \nUser\n.\nself\n,\n \ndatabase\n:\n \n.\nsqlite\n)\n\n\nservices\n.\nregister\n(\nmigrations\n)\n\n\n\n/// Other services....\n\n\n\n\n\n\nRegistering the provider will add all of the services required for Fluent SQLite to work properly. It also includes a default database config struct that uses typical development environment credentials. \n\n\nYou can of course override this config struct if you have non-standard credentials.\n\n\n/// Register custom SQLite Config\n\n\nlet\n \nsqliteConfig\n \n=\n \nSQLiteDatabaseConfig\n(\nhostname\n:\n \nlocalhost\n,\n \nport\n:\n \n5432\n,\n \nusername\n:\n \nvapor\n)\n\n\nservices\n.\nregister\n(\nsqliteConfig\n)\n\n\n\n\n\n\nOnce you have the \nMigrationConfig\n added, you should be able to run your application and see the following:\n\n\nMigrating sqlite DB\nMigrations \ncomplete\n\nServer starting on http://localhost:8080\n\n\n\n\n\nQuery\n\n\nNow that you have created a model and a corresponding schema in your database, let's make your first query.\n\n\nrouter\n.\nget\n(\nusers\n)\n \n{\n \nreq\n \nin\n\n \nreturn\n \nUser\n.\nquery\n(\non\n:\n \nreq\n).\nall\n()\n\n\n}\n\n\n\n\n\n\nIf you run your app, and query that route, you should see an empty array returned. Now you just need to add some users! Congratulations on getting your first Fluent SQLite model and migration working.\n\n\nConnection\n\n\nWith Fluent, you always have access to the underlying database driver. Using this underlying driver to perform a query is sometimes called a \"raw query\".\n\n\nLet's take a look at a raw SQLite query.\n\n\nrouter\n.\nget\n(\nsqlite-version\n)\n \n{\n \nreq\n \n-\n \nFuture\nString\n \nin\n\n \nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nsqlite\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nselect sqlite_version() as v;\n).\nmap\n(\nto\n:\n \nString\n.\nself\n)\n \n{\n \nrows\n \nin\n\n \nreturn\n \ntry\n \nrows\n[\n0\n].\nfirstValue\n(\nforColumn\n:\n \nv\n)?.\ndecode\n(\nString\n.\nself\n)\n \n??\n \nn/a\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nIn the above example, \nwithPooledConnection(to:)\n is used to create a connection to the database identified by \n.sqlite\n. This is the default database identifier. See \nFluent \n Database\n to learn more.\n\n\nOnce we have the \nSQLiteConnection\n, we can perform a query on it. You can learn more about the methods available in \nSQLite \n Core\n.",
|
|
"title": "Fluent SQLite"
|
|
},
|
|
{
|
|
"location": "/sqlite/fluent/#fluent-sqlite",
|
|
"text": "Fluent SQLite ( vapor/fluent-sqlite ) is a type-safe, fast, and easy-to-use ORM for SQLite built on top of Fluent . Seealso The Fluent SQLite package is built on top of Fluent and the pure Swift, NIO-based SQLite core . You should refer to their guides for more information about subjects not covered here.",
|
|
"title": "Fluent SQLite"
|
|
},
|
|
{
|
|
"location": "/sqlite/fluent/#getting-started",
|
|
"text": "This section will show you how to add Fluent SQLite to your project and create your first SQLiteModel .",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/sqlite/fluent/#package",
|
|
"text": "The first step to using Fluent SQLite is adding it as a dependency to your project in your SPM package manifest file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : MyApp , \n dependencies : [ \n /// Any other dependencies ... \n\n // \ud83d\udd8b\ud83d\udc2c Swift ORM (queries, models, relations, etc) built on SQLite. \n . package ( url : https://github.com/vapor/fluent-sqlite.git , from : 3.0.0-rc ), \n ], \n targets : [ \n . target ( name : App , dependencies : [ FluentSQLite , ...]), \n . target ( name : Run , dependencies : [ App ]), \n . testTarget ( name : AppTests , dependencies : [ App ]), \n ] ) Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command: vapor xcode",
|
|
"title": "Package"
|
|
},
|
|
{
|
|
"location": "/sqlite/fluent/#model",
|
|
"text": "Now let's create our first SQLiteModel . Models represent tables in your SQLite database and they are the primary method of interacting with your data. /// A simple user. final class User : SQLiteModel { \n /// The unique identifier for this user. \n var id : Int ? \n\n /// The user s full name. \n var name : String \n\n /// The user s current age in years. \n var age : Int \n\n /// Creates a new user. \n init ( id : Int ? = nil , name : String , age : Int ) { \n self . id = id \n self . name = name \n self . age = age \n } } The example above shows a SQLiteModel for a simple model representing a user. You can make both struct s and class es a model. You can even conform types that come from external modules. The only requirement is that these types conform to Codable , which must be declared on the base type for synthesized (automatic) conformance. Standard practice with SQLite databases is using an auto-generated INTEGER for creating and storing unique identifiers in the id column. It's also possible to use UUID s or even String s for your identifiers. There are convenience protocol for that. protocol type key SQLiteModel Int id SQLiteUUIDModel UUID id SQLiteStringModel String id Seealso Take a look at Fluent Model for more information on creating models with custom ID types and keys.",
|
|
"title": "Model"
|
|
},
|
|
{
|
|
"location": "/sqlite/fluent/#migration",
|
|
"text": "All of your models (with some rare exceptions) should have a corresponding table or schema in your database. You can use a Fluent Migration to automatically generate this schema in a testable, maintainable way. Fluent makes it easy to automatically generate a migration for your model Tip If you are creating models to represent an existing table or database, you can skip this step. /// Allows `User` to be used as a migration. extension User : Migration { } That's all it takes. Fluent uses Codable to analyze your model and will attempt to create the best possible schema for it. Take a look at Fluent Migration if you are interested in customizing this migration.",
|
|
"title": "Migration"
|
|
},
|
|
{
|
|
"location": "/sqlite/fluent/#configure",
|
|
"text": "The final step is to configure your database. At a minimum, this requires adding two things to your configure.swift file. FluentSQLiteProvider MigrationConfig Let's take a look. import FluentSQLite /// ... /// Register providers first try services . register ( FluentSQLiteProvider ()) /// Configure migrations var migrations = MigrationConfig () migrations . add ( model : User . self , database : . sqlite ) services . register ( migrations ) /// Other services.... Registering the provider will add all of the services required for Fluent SQLite to work properly. It also includes a default database config struct that uses typical development environment credentials. You can of course override this config struct if you have non-standard credentials. /// Register custom SQLite Config let sqliteConfig = SQLiteDatabaseConfig ( hostname : localhost , port : 5432 , username : vapor ) services . register ( sqliteConfig ) Once you have the MigrationConfig added, you should be able to run your application and see the following: Migrating sqlite DB\nMigrations complete \nServer starting on http://localhost:8080",
|
|
"title": "Configure"
|
|
},
|
|
{
|
|
"location": "/sqlite/fluent/#query",
|
|
"text": "Now that you have created a model and a corresponding schema in your database, let's make your first query. router . get ( users ) { req in \n return User . query ( on : req ). all () } If you run your app, and query that route, you should see an empty array returned. Now you just need to add some users! Congratulations on getting your first Fluent SQLite model and migration working.",
|
|
"title": "Query"
|
|
},
|
|
{
|
|
"location": "/sqlite/fluent/#connection",
|
|
"text": "With Fluent, you always have access to the underlying database driver. Using this underlying driver to perform a query is sometimes called a \"raw query\". Let's take a look at a raw SQLite query. router . get ( sqlite-version ) { req - Future String in \n return req . withPooledConnection ( to : . sqlite ) { conn in \n return try conn . query ( select sqlite_version() as v; ). map ( to : String . self ) { rows in \n return try rows [ 0 ]. firstValue ( forColumn : v )?. decode ( String . self ) ?? n/a \n } \n } } In the above example, withPooledConnection(to:) is used to create a connection to the database identified by .sqlite . This is the default database identifier. See Fluent Database to learn more. Once we have the SQLiteConnection , we can perform a query on it. You can learn more about the methods available in SQLite Core .",
|
|
"title": "Connection"
|
|
},
|
|
{
|
|
"location": "/sqlite/core/",
|
|
"text": "SQLite Core\n\n\nSQLite (\nvapor/sqlite\n) is a wrapper around the \nlibsqlite\n C-library.\n\n\n\n\nSeealso\n\n\nThe higher-level, Fluent SQLite ORM guide is located at \nSQLite \n Fluent\n\n\n\n\nUsing just the SQLite package for your project may be a good idea if any of the following are true.\n\n\n\n\nYou have an existing DB with non-standard structure.\n\n\nYou rely heavily on custom or complex SQL queries.\n\n\nYou just plain don't like ORMs.\n\n\n\n\nSQLite core is built on top of DatabaseKit which provides some conveniences like connection pooling and integrations with Vapor's \nServices\n architecture.\n\n\n\n\nTip\n\n\nEven if you do choose to use \nFluent SQLite\n, all of the features of SQLite core will be available to you.\n\n\n\n\nGetting Started\n\n\nLet's take a look at how you can get started using SQLite core.\n\n\nPackage\n\n\nThe first step to using SQLite core is adding it as a dependency to your project in your SPM package manifest file.\n\n\n// swift-tools-version:4.0\n\n\nimport\n \nPackageDescription\n\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/// Any other dependencies ...\n\n\n \n// \ud83d\udd35 SQLite 3 wrapper for Swift.\n\n \n.\npackage\n(\nurl\n:\n \nhttps://github.com/vapor/sqlite.git\n,\n \nfrom\n:\n \n3.0.0-rc\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nApp\n,\n \ndependencies\n:\n \n[\nSQLite\n,\n \n...]),\n\n \n.\ntarget\n(\nname\n:\n \nRun\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n.\ntestTarget\n(\nname\n:\n \nAppTests\n,\n \ndependencies\n:\n \n[\nApp\n]),\n\n \n]\n\n\n)\n\n\n\n\n\n\nDon't forget to add the module as a dependency in the \ntargets\n array. Once you have added the dependency, regenerate your Xcode project with the following command:\n\n\nvapor xcode\n\n\n\n\n\nConfig\n\n\nThe next step is to configure the database in your \nconfigure.swift\n file.\n\n\nimport\n \nSQLite\n\n\n\n/// ...\n\n\n\n/// Register providers first\n\n\ntry\n \nservices\n.\nregister\n(\nSQLiteProvider\n())\n\n\n\n\n\n\nRegistering the provider will add all of the services required for SQLite to work properly. It also includes a default database config struct that uses an in-memory DB.\n\n\nYou can of course override this config struct if you have non-standard credentials.\n\n\n/// Register custom SQLite Config\n\n\nlet\n \nsqliteConfig\n \n=\n \nSQLiteDatabaseConfig\n(\nstorage\n:\n \n.\nmemory\n)\n\n\nservices\n.\nregister\n(\nsqliteConfig\n)\n\n\n\n\n\n\nQuery\n\n\nNow that the database is configured, you can make your first query.\n\n\nrouter\n.\nget\n(\nsqlite-version\n)\n \n{\n \nreq\n \n-\n \nFuture\nString\n \nin\n\n \nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nsqlite\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nselect sqlite_version() as v;\n).\nmap\n(\nto\n:\n \nString\n.\nself\n)\n \n{\n \nrows\n \nin\n\n \nreturn\n \ntry\n \nrows\n[\n0\n].\nfirstValue\n(\nforColumn\n:\n \nv\n)?.\ndecode\n(\nString\n.\nself\n)\n \n??\n \nn/a\n\n \n}\n\n \n}\n\n\n}\n\n\n\n\n\n\nVisiting this route should display your SQLite version.\n\n\nConnection\n\n\nA \nSQLiteConnection\n is normally created using the \nRequest\n container and can perform two different types of queries.\n\n\nCreate\n\n\nThere are two methods for creating a \nSQLiteConnection\n.\n\n\nreturn\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nsqlite\n)\n \n{\n \nconn\n \nin\n\n \n/// ...\n\n\n}\n\n\nreturn\n \nreq\n.\nwithConnection\n(\nto\n:\n \n.\nsqlite\n)\n \n{\n \nconn\n \nin\n\n \n/// ...\n\n\n}\n\n\n\n\n\n\nAs the names imply, \nwithPooledConnection(to:)\n utilizes a connection pool. \nwithConnection(to:)\n does not. Connection pooling is a great way to ensure your application does not exceed the limits of your database, even under peak load.\n\n\nSimply Query\n\n\nUse \n.simpleQuery(_:)\n to perform a query on your SQLite database that does not bind any parameters. Some queries you send to SQLite may actually require that you use the \nsimpleQuery(_:)\n method instead of the parameterized method. \n\n\n\n\nNote\n\n\nThis method sends and receives data as text-encoded, meaning it is not optimal for transmitting things like integers.\n\n\n\n\nlet\n \nrows\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nsqlite\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \nconn\n.\nsimpleQuery\n(\nSELECT * FROM users;\n)\n\n\n}\n\n\nprint\n(\nrows\n)\n \n// Future\n[[SQLiteColumn: SQLiteData]]\n\n\n\n\n\n\nYou can also choose to receive each row in a callback, which is great for conserving memory for large queries.\n\n\nlet\n \ndone\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nsqlite\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \nconn\n.\nsimpleQuery\n(\nSELECT * FROM users;\n)\n \n{\n \nrow\n \nin\n\n \nprint\n(\nrow\n)\n \n// [SQLiteColumn: SQLiteData]\n\n \n}\n\n\n}\n\n\nprint\n(\ndone\n)\n \n// Future\nVoid\n\n\n\n\n\n\nParameterized Query\n\n\nSQLite also supports sending parameterized queries (sometimes called prepared statements). This method allows you to insert data placeholders into the SQL string and send the values separately.\n\n\nData sent via parameterized queries is binary encoded, making it more efficient for sending some data types. In general, you should use parameterized queries where ever possible.\n\n\nlet\n \nusers\n \n=\n \nreq\n.\nwithPooledConnection\n(\nto\n:\n \n.\nsqlite\n)\n \n{\n \nconn\n \nin\n\n \nreturn\n \ntry\n \nconn\n.\nquery\n(\nSELECT * users WHERE name = $1;\n,\n \n[\nVapor\n])\n\n\n}\n\n\nprint\n(\nusers\n)\n \n// Future\n[[SQLiteColumn: SQLiteData]]\n\n\n\n\n\n\nYou can also provide a callback, similar to simple queries, for handling each row individually.",
|
|
"title": "SQLite Core"
|
|
},
|
|
{
|
|
"location": "/sqlite/core/#sqlite-core",
|
|
"text": "SQLite ( vapor/sqlite ) is a wrapper around the libsqlite C-library. Seealso The higher-level, Fluent SQLite ORM guide is located at SQLite Fluent Using just the SQLite package for your project may be a good idea if any of the following are true. You have an existing DB with non-standard structure. You rely heavily on custom or complex SQL queries. You just plain don't like ORMs. SQLite core is built on top of DatabaseKit which provides some conveniences like connection pooling and integrations with Vapor's Services architecture. Tip Even if you do choose to use Fluent SQLite , all of the features of SQLite core will be available to you.",
|
|
"title": "SQLite Core"
|
|
},
|
|
{
|
|
"location": "/sqlite/core/#getting-started",
|
|
"text": "Let's take a look at how you can get started using SQLite core.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/sqlite/core/#package",
|
|
"text": "The first step to using SQLite core is adding it as a dependency to your project in your SPM package manifest file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : MyApp , \n dependencies : [ \n /// Any other dependencies ... \n\n // \ud83d\udd35 SQLite 3 wrapper for Swift. \n . package ( url : https://github.com/vapor/sqlite.git , from : 3.0.0-rc ), \n ], \n targets : [ \n . target ( name : App , dependencies : [ SQLite , ...]), \n . target ( name : Run , dependencies : [ App ]), \n . testTarget ( name : AppTests , dependencies : [ App ]), \n ] ) Don't forget to add the module as a dependency in the targets array. Once you have added the dependency, regenerate your Xcode project with the following command: vapor xcode",
|
|
"title": "Package"
|
|
},
|
|
{
|
|
"location": "/sqlite/core/#config",
|
|
"text": "The next step is to configure the database in your configure.swift file. import SQLite /// ... /// Register providers first try services . register ( SQLiteProvider ()) Registering the provider will add all of the services required for SQLite to work properly. It also includes a default database config struct that uses an in-memory DB. You can of course override this config struct if you have non-standard credentials. /// Register custom SQLite Config let sqliteConfig = SQLiteDatabaseConfig ( storage : . memory ) services . register ( sqliteConfig )",
|
|
"title": "Config"
|
|
},
|
|
{
|
|
"location": "/sqlite/core/#query",
|
|
"text": "Now that the database is configured, you can make your first query. router . get ( sqlite-version ) { req - Future String in \n return req . withPooledConnection ( to : . sqlite ) { conn in \n return try conn . query ( select sqlite_version() as v; ). map ( to : String . self ) { rows in \n return try rows [ 0 ]. firstValue ( forColumn : v )?. decode ( String . self ) ?? n/a \n } \n } } Visiting this route should display your SQLite version.",
|
|
"title": "Query"
|
|
},
|
|
{
|
|
"location": "/sqlite/core/#connection",
|
|
"text": "A SQLiteConnection is normally created using the Request container and can perform two different types of queries.",
|
|
"title": "Connection"
|
|
},
|
|
{
|
|
"location": "/sqlite/core/#create",
|
|
"text": "There are two methods for creating a SQLiteConnection . return req . withPooledConnection ( to : . sqlite ) { conn in \n /// ... } return req . withConnection ( to : . sqlite ) { conn in \n /// ... } As the names imply, withPooledConnection(to:) utilizes a connection pool. withConnection(to:) does not. Connection pooling is a great way to ensure your application does not exceed the limits of your database, even under peak load.",
|
|
"title": "Create"
|
|
},
|
|
{
|
|
"location": "/sqlite/core/#simply-query",
|
|
"text": "Use .simpleQuery(_:) to perform a query on your SQLite database that does not bind any parameters. Some queries you send to SQLite may actually require that you use the simpleQuery(_:) method instead of the parameterized method. Note This method sends and receives data as text-encoded, meaning it is not optimal for transmitting things like integers. let rows = req . withPooledConnection ( to : . sqlite ) { conn in \n return conn . simpleQuery ( SELECT * FROM users; ) } print ( rows ) // Future [[SQLiteColumn: SQLiteData]] You can also choose to receive each row in a callback, which is great for conserving memory for large queries. let done = req . withPooledConnection ( to : . sqlite ) { conn in \n return conn . simpleQuery ( SELECT * FROM users; ) { row in \n print ( row ) // [SQLiteColumn: SQLiteData] \n } } print ( done ) // Future Void",
|
|
"title": "Simply Query"
|
|
},
|
|
{
|
|
"location": "/sqlite/core/#parameterized-query",
|
|
"text": "SQLite also supports sending parameterized queries (sometimes called prepared statements). This method allows you to insert data placeholders into the SQL string and send the values separately. Data sent via parameterized queries is binary encoded, making it more efficient for sending some data types. In general, you should use parameterized queries where ever possible. let users = req . withPooledConnection ( to : . sqlite ) { conn in \n return try conn . query ( SELECT * users WHERE name = $1; , [ Vapor ]) } print ( users ) // Future [[SQLiteColumn: SQLiteData]] You can also provide a callback, similar to simple queries, for handling each row individually.",
|
|
"title": "Parameterized Query"
|
|
},
|
|
{
|
|
"location": "/template-kit/getting-started/",
|
|
"text": "Getting Started with Template Kit\n\n\nTemplate Kit (\nvapor/template-kit\n) is a framework for implementing templating languages in Swift. It is currently used to power Leaf (\nvapor/leaf\n) and hopefully more languages in the future.\n\n\nTemplate Kit is designed to make implementing a templating language easy by defining a common template structure and handling the entire serialization step.\n\n\n\n\nWarning\n\n\nThese docs are for developers interested in implementing a templating language using Template Kit. See \nLeaf \n Getting Started\n for information about using Leaf.\n\n\n\n\nVapor\n\n\nThis package is included with Vapor and exported by default. You will have access to all \nTemplateKit\n APIs when you import \nVapor\n.\n\n\nimport\n \nVapor\n\n\n\n\n\n\nStandalone\n\n\nThe Template Kit package is lightweight, pure-Swift, and has very few dependencies. This means it can be used as a templating framework for any Swift project\neven one not using Vapor.\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\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/template-kit.git\n,\n \nfrom\n:\n \n1.0.0\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nTemplateKit\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport TemplateKit\n to access the APIs.\n\n\nOverview\n\n\nLet's take a look at how Leaf uses Template Kit to render views.\n\n\nAssume we have a template \ngreeting.leaf\n with the following contents:\n\n\nHello, #capitalize(name)!\n\n\n\n\n\nThis first step in rendering this view is to parse the syntax into an abstract syntax tree (AST). This is the part of view rendering that Leaf is responsible for, since Leaf has a unique syntax.\n\n\nLeaf does this by creating a \nLeafParser\n that conforms to \nTemplateParser\n. \n\n\ngreeting.leaf -\n LeafParser -\n AST\n\n\n\n\n\nIn code, this looks like:\n\n\nfunc\n \nparse\n(\nscanner\n:\n \nTemplateByteScanner\n)\n \nthrows\n \n-\n \n[\nTemplateSyntax\n]\n\n\n\n\n\n\nThe AST for our example \ngreeting.leaf\n file would look something like this:\n\n\n[\n\n \n.\nraw\n(\ndata\n:\n \nHello. \n),\n \n \n.\ntag\n(\n\n \nname\n:\n \ncapitalize\n,\n \n \nparameters\n:\n \n[.\nidentifier\n(\nname\n)]\n\n \n),\n\n \n.\nraw\n(\ndata\n:\n \n!\n),\n \n\n]\n\n\n\n\n\n\nNow that Leaf has created an AST, it's job is done! Template Kit will handle converting this AST into a rendered view. All it needs is a \nTemplateData\n to use for filling in any variables.\n\n\nlet\n \ndata\n \n=\n \nTemplateData\n.\ndictionary\n([\nname\n:\n \nvapor\n])\n\n\n\n\n\n\nThe above data will be combined with the AST and used by the \nTemplateSerializer\n to create a rendered view.\n\n\nAST + Data -\n TemplateSerializer -\n View\n\n\n\n\n\nOur rendered view will look something like:\n\n\nHello, Vapor!\n\n\n\n\n\nAll of these steps are handled by \nLeafRenderer\n which conforms to \nTemplateRenderer\n. A template renderer is simply an object that contains both a parser and a serializer. When you implement one, you will get several helpful extensions from Template Kit for free that help load files and cache parsed ASTs. It's what the end user will use to render views.\n\n\nThe entire pipeline looks like this:\n\n\n LeafRenderer\n |\n|----------------------------------------------------------------|\n greeting.leaf -\n LeafParser -\n AST -\n TemplateSerializer -\n View\n ^\n /\n TemplateData\n\n\n\n\n\nIn code, the \nmethod\n looks like this:\n\n\npublic\n \nfunc\n \nrender\n(\n_\n \npath\n:\n \nString\n,\n \n_\n \ncontext\n:\n \nTemplateData\n)\n \n-\n \nFuture\nView\n\n\n\n\n\n\nCheck out Template Kit's \nAPI docs\n for detailed information about all of the protocols, structs, and classes Template Kit offers.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/template-kit/getting-started/#getting-started-with-template-kit",
|
|
"text": "Template Kit ( vapor/template-kit ) is a framework for implementing templating languages in Swift. It is currently used to power Leaf ( vapor/leaf ) and hopefully more languages in the future. Template Kit is designed to make implementing a templating language easy by defining a common template structure and handling the entire serialization step. Warning These docs are for developers interested in implementing a templating language using Template Kit. See Leaf Getting Started for information about using Leaf.",
|
|
"title": "Getting Started with Template Kit"
|
|
},
|
|
{
|
|
"location": "/template-kit/getting-started/#vapor",
|
|
"text": "This package is included with Vapor and exported by default. You will have access to all TemplateKit APIs when you import Vapor . import Vapor",
|
|
"title": "Vapor"
|
|
},
|
|
{
|
|
"location": "/template-kit/getting-started/#standalone",
|
|
"text": "The Template Kit package is lightweight, pure-Swift, and has very few dependencies. This means it can be used as a templating framework for any Swift project even one not using Vapor. To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . package ( url : https://github.com/vapor/template-kit.git , from : 1.0.0 ), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ TemplateKit , ... ]) \n ] ) Use import TemplateKit to access the APIs.",
|
|
"title": "Standalone"
|
|
},
|
|
{
|
|
"location": "/template-kit/getting-started/#overview",
|
|
"text": "Let's take a look at how Leaf uses Template Kit to render views. Assume we have a template greeting.leaf with the following contents: Hello, #capitalize(name)! This first step in rendering this view is to parse the syntax into an abstract syntax tree (AST). This is the part of view rendering that Leaf is responsible for, since Leaf has a unique syntax. Leaf does this by creating a LeafParser that conforms to TemplateParser . greeting.leaf - LeafParser - AST In code, this looks like: func parse ( scanner : TemplateByteScanner ) throws - [ TemplateSyntax ] The AST for our example greeting.leaf file would look something like this: [ \n . raw ( data : Hello. ), \n . tag ( \n name : capitalize , \n parameters : [. identifier ( name )] \n ), \n . raw ( data : ! ), ] Now that Leaf has created an AST, it's job is done! Template Kit will handle converting this AST into a rendered view. All it needs is a TemplateData to use for filling in any variables. let data = TemplateData . dictionary ([ name : vapor ]) The above data will be combined with the AST and used by the TemplateSerializer to create a rendered view. AST + Data - TemplateSerializer - View Our rendered view will look something like: Hello, Vapor! All of these steps are handled by LeafRenderer which conforms to TemplateRenderer . A template renderer is simply an object that contains both a parser and a serializer. When you implement one, you will get several helpful extensions from Template Kit for free that help load files and cache parsed ASTs. It's what the end user will use to render views. The entire pipeline looks like this: LeafRenderer\n |\n|----------------------------------------------------------------|\n greeting.leaf - LeafParser - AST - TemplateSerializer - View\n ^\n /\n TemplateData In code, the method looks like this: public func render ( _ path : String , _ context : TemplateData ) - Future View Check out Template Kit's API docs for detailed information about all of the protocols, structs, and classes Template Kit offers.",
|
|
"title": "Overview"
|
|
},
|
|
{
|
|
"location": "/testing/getting-started/",
|
|
"text": "Getting Started with Testing\n\n\nComing soon.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/testing/getting-started/#getting-started-with-testing",
|
|
"text": "Coming soon.",
|
|
"title": "Getting Started with Testing"
|
|
},
|
|
{
|
|
"location": "/url-encoded-form/getting-started/",
|
|
"text": "Getting Started with URL-Encoded Form\n\n\nURL-Encoded Form (\nvapor/url-encoded-form\n) is a small package that helps you parse and serialize \napplication/x-www-form-urlencoded\n data. URL-encoded forms are a widely-supported encoding on the web. It's most often used for serializing web forms sent via POST requests.\n\n\nThe URL-Encoded Form package makes it easy to use this encoding by integrating directly with \nCodable\n.\n\n\nVapor\n\n\nThis package is included with Vapor and exported by default. You will have access to all \nURLEncodedForm\n APIs when you import \nVapor\n.\n\n\nimport\n \nVapor\n\n\n\n\n\n\nStandalone\n\n\nThe URL-Encoded Form package is lightweight, pure-Swift, and has very few dependencies. This means it can be used to work with \nform-urlencoded\n data for any Swift project\neven one not using Vapor.\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\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/url-encoded-form.git\n,\n \nfrom\n:\n \n1.0.0\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nURLEncodedForm\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport URLEncodedForm\n to access the APIs.\n\n\n\n\nWarning\n\n\nSome of this guide may contain Vapor-specific APIs, however most of it should be applicable to the URL-Encoded Form package in general.\nVisit the \nAPI Docs\n for specific API info.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/url-encoded-form/getting-started/#getting-started-with-url-encoded-form",
|
|
"text": "URL-Encoded Form ( vapor/url-encoded-form ) is a small package that helps you parse and serialize application/x-www-form-urlencoded data. URL-encoded forms are a widely-supported encoding on the web. It's most often used for serializing web forms sent via POST requests. The URL-Encoded Form package makes it easy to use this encoding by integrating directly with Codable .",
|
|
"title": "Getting Started with URL-Encoded Form"
|
|
},
|
|
{
|
|
"location": "/url-encoded-form/getting-started/#vapor",
|
|
"text": "This package is included with Vapor and exported by default. You will have access to all URLEncodedForm APIs when you import Vapor . import Vapor",
|
|
"title": "Vapor"
|
|
},
|
|
{
|
|
"location": "/url-encoded-form/getting-started/#standalone",
|
|
"text": "The URL-Encoded Form package is lightweight, pure-Swift, and has very few dependencies. This means it can be used to work with form-urlencoded data for any Swift project even one not using Vapor. To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . package ( url : https://github.com/vapor/url-encoded-form.git , from : 1.0.0 ), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ URLEncodedForm , ... ]) \n ] ) Use import URLEncodedForm to access the APIs. Warning Some of this guide may contain Vapor-specific APIs, however most of it should be applicable to the URL-Encoded Form package in general.\nVisit the API Docs for specific API info.",
|
|
"title": "Standalone"
|
|
},
|
|
{
|
|
"location": "/url-encoded-form/overview/",
|
|
"text": "Using URL-Encoded Form\n\n\nURL-Encoded Form is a widely-supported encoding on the web. It's most often used for serializing web forms sent via POST requests. This encoding is also used to send structured data in URL query strings.\n\n\nIt is a relatively efficient encoding for sending small amounts of data. However, all data must be percent-encoded making this encoding suboptimal for large amounts of data. See the \nMultipart\n encoding if you need to upload things like files.\n\n\n\n\nTip\n\n\nURL-Encoded Form integrates with \nContent\n like all other encoding methods in Vapor. See \nVapor \n Content\n for more information about the \nContent\n protocol. \n\n\n\n\nLet's take a look at how to decode a \napplication/x-www-form-urlencoded\n request. \n\n\nDecode Body\n\n\nMost often, you will be decoding \nform-urlencoded\n-encoded requests from a web form. Let's take a look at what one of these requests might look like. After that, we will take a look at what the HTML form for that request would look like.\n\n\nRequest\n\n\nHere is an example \nform-urlencoded\n-encoded request for creating a new user.\n\n\nPOST\n \n/users\n \nHTTP\n/\n1.1\n\n\nContent-Type\n:\n \napplication/x-www-form-urlencoded\n\n\nname=Vapor\nage=3\nluckyNumbers[]=5\nluckyNumbers[]=7\n\n\n\n\n\nYou can see the \n[]\n notation is used to encode arrays. Your web form will need to use this notation as well.\n\n\nForm\n\n\nThere are many ways to create a \nform-urlencoded\n-encoded request, but the most common is an HTML web form. Here is what the HTML form for this request might have looked like.\n\n\nform\n \nmethod\n=\nPOST\n \naction\n=\n/users\n\n \ninput\n \ntype\n=\ntext\n \nname\n=\nname\n\n \ninput\n \ntype\n=\ntext\n \nname\n=\nage\n\n \ninput\n \ntype\n=\ntext\n \nname\n=\nluckyNumbers[]\n\n \ninput\n \ntype\n=\ntext\n \nname\n=\nluckyNumbers[]\n \n\n/\nform\n\n\n\n\n\n\nSince we are not specifying a special \nenctype\n attribute on the \nform\n, the browser will URL-encode the form by default. We are also providing two fields with the same name, \nluckyNumbers[]\n. This will let us send an array of values.\n\n\nContent\n\n\nNow let's take a look at how we would handle this request in Vapor. The first step (as always with \nContent\n) is to create a \nCodable\n struct that represents the data structure.\n\n\nimport\n \nVapor\n\n\n\nstruct\n \nUser\n:\n \nContent\n \n{\n\n \nvar\n \nname\n:\n \nString\n\n \nvar\n \nage\n:\n \nInt\n\n \nvar\n \nluckyNumbers\n:\n \n[\nInt\n]\n\n\n}\n\n\n\n\n\n\nNow that we have our \nUser\n struct, let's decode that request! We can use the \nContentContainer\n to do this easily.\n\n\nrouter\n.\npost\n(\nusers\n)\n \n{\n \nreq\n \n-\n \nFuture\nHTTPStatus\n \nin\n\n \nreturn\n \ntry\n \nreq\n.\ncontent\n.\ndecode\n(\nUser\n.\nself\n).\nmap\n(\nto\n:\n \nHTTPStatus\n.\nself\n)\n \n{\n \nuser\n \nin\n\n \nprint\n(\nuser\n.\nname\n)\n \n// \nVapor\n\n \nprint\n(\nuser\n.\nage\n)\n \n// 3\n\n \nprint\n(\nuser\n.\nluckyNumbers\n)\n \n// [5, 7]\n\n \nreturn\n \n.\nok\n\n \n}\n\n\n}\n\n\n\n\n\n\nNow when you post the form to \n/users\n, you should see the information printed in the console. Nice work!\n\n\nEncode Body\n\n\nAPIs encode \nform-urlencoded\n data much less often than they decode it. However, encoding is just as easy with Vapor. Using our same \nUser\n struct from the previous example, here is how we can encode a \nform-urlencoded\n-encoded response.\n\n\nrouter\n.\nget\n(\nmultipart\n)\n \n{\n \nreq\n \n-\n \nUser\n \nin\n\n \nlet\n \nres\n \n=\n \nreq\n.\nmakeResponse\n()\n\n \nlet\n \nuser\n \n=\n \nUser\n(\nname\n:\n \nVapor\n,\n \nage\n:\n \n3\n,\n \nluckyNumbers\n:\n \n[\n5\n,\n \n7\n])\n\n \nres\n.\ncontent\n.\nencode\n(\nuser\n,\n \nas\n:\n \n.\nurlEncodedForm\n)\n\n \nreturn\n \nuser\n\n\n}\n\n\n\n\n\n\n\n\nTip\n\n\nIf you set a default \nMediaType\n on your \nContent\n types, then you can return them directly in the route closure.\n\n\n\n\nURL Query\n\n\nURL-Encoded Forms are also useful for sending structured data in the URL query string. This is widely used for sending data via GET requests where HTTP bodies are not allowed.\n\n\nLet's take a look at how we can decode some search parameters from the query string.\n\n\nGET\n \n/users?name=Vapor\nage=3\n \nHTTP\n/\n1.1\n\n\n\n\n\n\nThe first step (as always with \nContent\n) is to create a \nCodable\n struct that represents the data structure.\n\n\nimport\n \nVapor\n\n\n\nstruct\n \nUsersFilters\n:\n \nContent\n \n{\n\n \nvar\n \nname\n:\n \nString\n?\n\n \nvar\n \nage\n:\n \nInt\n?\n\n\n}\n\n\n\n\n\n\nHere we are making both \nname\n and \nage\n optional since the route can be called without any flags to return all users.\n\n\nNow that we have a \nCodable\n struct, we can decode the URL query string. The process is almost identical to decoding content, expect we use \nreq.query\n instead of \nreq.content\n.\n\n\nrouter\n.\nget\n(\nusers\n)\n \n{\n \nreq\n \n-\n \nFuture\n[\nUser\n]\n \nin\n\n \nlet\n \nfilters\n \n=\n \ntry\n \nreq\n.\nquery\n.\ndecode\n(\nUsersFilters\n.\nself\n)\n\n \nprint\n(\nfilters\n.\nname\n)\n \n// Vapor\n\n \nprint\n(\nfilters\n.\nage\n)\n \n// 3\n\n \nreturn\n \n// fetch users with filters\n\n\n}\n\n\n\n\n\n\n\n\nTip\n\n\nDecoding the URL query string is not asynchronous because, unlike HTTP bodies, Vapor can be sure it is available when calling the route closure.",
|
|
"title": "Overview"
|
|
},
|
|
{
|
|
"location": "/url-encoded-form/overview/#using-url-encoded-form",
|
|
"text": "URL-Encoded Form is a widely-supported encoding on the web. It's most often used for serializing web forms sent via POST requests. This encoding is also used to send structured data in URL query strings. It is a relatively efficient encoding for sending small amounts of data. However, all data must be percent-encoded making this encoding suboptimal for large amounts of data. See the Multipart encoding if you need to upload things like files. Tip URL-Encoded Form integrates with Content like all other encoding methods in Vapor. See Vapor Content for more information about the Content protocol. Let's take a look at how to decode a application/x-www-form-urlencoded request.",
|
|
"title": "Using URL-Encoded Form"
|
|
},
|
|
{
|
|
"location": "/url-encoded-form/overview/#decode-body",
|
|
"text": "Most often, you will be decoding form-urlencoded -encoded requests from a web form. Let's take a look at what one of these requests might look like. After that, we will take a look at what the HTML form for that request would look like.",
|
|
"title": "Decode Body"
|
|
},
|
|
{
|
|
"location": "/url-encoded-form/overview/#request",
|
|
"text": "Here is an example form-urlencoded -encoded request for creating a new user. POST /users HTTP / 1.1 Content-Type : application/x-www-form-urlencoded \n\nname=Vapor age=3 luckyNumbers[]=5 luckyNumbers[]=7 You can see the [] notation is used to encode arrays. Your web form will need to use this notation as well.",
|
|
"title": "Request"
|
|
},
|
|
{
|
|
"location": "/url-encoded-form/overview/#form",
|
|
"text": "There are many ways to create a form-urlencoded -encoded request, but the most common is an HTML web form. Here is what the HTML form for this request might have looked like. form method = POST action = /users \n input type = text name = name \n input type = text name = age \n input type = text name = luckyNumbers[] \n input type = text name = luckyNumbers[] / form Since we are not specifying a special enctype attribute on the form , the browser will URL-encode the form by default. We are also providing two fields with the same name, luckyNumbers[] . This will let us send an array of values.",
|
|
"title": "Form"
|
|
},
|
|
{
|
|
"location": "/url-encoded-form/overview/#content",
|
|
"text": "Now let's take a look at how we would handle this request in Vapor. The first step (as always with Content ) is to create a Codable struct that represents the data structure. import Vapor struct User : Content { \n var name : String \n var age : Int \n var luckyNumbers : [ Int ] } Now that we have our User struct, let's decode that request! We can use the ContentContainer to do this easily. router . post ( users ) { req - Future HTTPStatus in \n return try req . content . decode ( User . self ). map ( to : HTTPStatus . self ) { user in \n print ( user . name ) // Vapor \n print ( user . age ) // 3 \n print ( user . luckyNumbers ) // [5, 7] \n return . ok \n } } Now when you post the form to /users , you should see the information printed in the console. Nice work!",
|
|
"title": "Content"
|
|
},
|
|
{
|
|
"location": "/url-encoded-form/overview/#encode-body",
|
|
"text": "APIs encode form-urlencoded data much less often than they decode it. However, encoding is just as easy with Vapor. Using our same User struct from the previous example, here is how we can encode a form-urlencoded -encoded response. router . get ( multipart ) { req - User in \n let res = req . makeResponse () \n let user = User ( name : Vapor , age : 3 , luckyNumbers : [ 5 , 7 ]) \n res . content . encode ( user , as : . urlEncodedForm ) \n return user } Tip If you set a default MediaType on your Content types, then you can return them directly in the route closure.",
|
|
"title": "Encode Body"
|
|
},
|
|
{
|
|
"location": "/url-encoded-form/overview/#url-query",
|
|
"text": "URL-Encoded Forms are also useful for sending structured data in the URL query string. This is widely used for sending data via GET requests where HTTP bodies are not allowed. Let's take a look at how we can decode some search parameters from the query string. GET /users?name=Vapor age=3 HTTP / 1.1 The first step (as always with Content ) is to create a Codable struct that represents the data structure. import Vapor struct UsersFilters : Content { \n var name : String ? \n var age : Int ? } Here we are making both name and age optional since the route can be called without any flags to return all users. Now that we have a Codable struct, we can decode the URL query string. The process is almost identical to decoding content, expect we use req.query instead of req.content . router . get ( users ) { req - Future [ User ] in \n let filters = try req . query . decode ( UsersFilters . self ) \n print ( filters . name ) // Vapor \n print ( filters . age ) // 3 \n return // fetch users with filters } Tip Decoding the URL query string is not asynchronous because, unlike HTTP bodies, Vapor can be sure it is available when calling the route closure.",
|
|
"title": "URL Query"
|
|
},
|
|
{
|
|
"location": "/validation/getting-started/",
|
|
"text": "Getting Started with Validation\n\n\nValidation (\nvapor/validation\n) is a framework for validating data sent to your application. It can help validate things like names, emails and more. It is also extensible, allowing you to easily create custom validators.\n\n\nThe rest of this guide will show you how to add and import the \nValidation\n module. For more information on using this package, check out \nValidation \n Overview\n.\n\n\nVapor\n\n\nThis package is included with Vapor and exported by default. You will have access to all \nValidation\n APIs when you import \nVapor\n.\n\n\nimport\n \nVapor\n\n\n\n\n\n\nStandalone\n\n\nThe Service package is lightweight, pure-Swift, and has very few dependencies. This means it can be used as a validation framework for any Swift project\neven one not using Vapor.\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\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 \nfrom\n:\n \n2.0.0\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nValidation\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Validation\n to access the APIs.\n\n\n\n\nWarning\n\n\nSome of this guide may contain Vapor-specific APIs, however most of it should be applicable to the Validation package in general.\nVisit the \nAPI Docs\n for Validation-specific API info.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/validation/getting-started/#getting-started-with-validation",
|
|
"text": "Validation ( vapor/validation ) is a framework for validating data sent to your application. It can help validate things like names, emails and more. It is also extensible, allowing you to easily create custom validators. The rest of this guide will show you how to add and import the Validation module. For more information on using this package, check out Validation Overview .",
|
|
"title": "Getting Started with Validation"
|
|
},
|
|
{
|
|
"location": "/validation/getting-started/#vapor",
|
|
"text": "This package is included with Vapor and exported by default. You will have access to all Validation APIs when you import Vapor . import Vapor",
|
|
"title": "Vapor"
|
|
},
|
|
{
|
|
"location": "/validation/getting-started/#standalone",
|
|
"text": "The Service package is lightweight, pure-Swift, and has very few dependencies. This means it can be used as a validation framework for any Swift project even one not using Vapor. To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . package ( url : https://github.com/vapor/validation.git , from : 2.0.0 ), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ Validation , ... ]) \n ] ) Use import Validation to access the APIs. Warning Some of this guide may contain Vapor-specific APIs, however most of it should be applicable to the Validation package in general.\nVisit the API Docs for Validation-specific API info.",
|
|
"title": "Standalone"
|
|
},
|
|
{
|
|
"location": "/validation/overview/",
|
|
"text": "Validation Overview\n\n\nValidation is a framework for validating data sent to your application. It can help validate things like names, emails and more. It is also extensible, allowing you to easily create custom validators.\n\n\nSwift \n Codable\n\n\nSwift's strong type system and \nCodable\n take care of most of the basic validation that web apps need to do. \n\n\nstruct\n \nUser\n:\n \nCodable\n \n{\n\n \nvar\n \nid\n:\n \nUUID\n?\n\n \nvar\n \nname\n:\n \nString\n\n \nvar\n \nage\n:\n \nInt\n\n \nvar\n \nemail\n:\n \nString\n?\n\n\n}\n\n\n\n\n\n\nFor example, when you decode the above \nUser\n model, Swift will automatically ensure the following:\n\n\n\n\nid\n is a valid \nUUID\n or is \nnil\n.\n\n\nname\n is a valid \nString\n and is \nnot\n \nnil\n.\n\n\nage\n is a valid \nInt\n and is \nnot\n \nnil\n.\n\n\nemail\n is a valid \nString\n or is \nnil\n.\n\n\n\n\nThis is a great first step, but there is still room for improvement here. Here are some examples of things Swift and \nCodable\n would not mind, but are not ideal:\n\n\n\n\nname\n is empty string \n\"\"\n\n\nname\n contains non-alphanumeric characters\n\n\nage\n is a negative number \n-42\n\n\nemail\n is not correctly formatted \ntest@@vapor.codes\n\n\n\n\nLuckily the Validation package can help.\n\n\nValidatable\n\n\nLet's take a look at how the Validation package can help you validate incoming data. We'll start by conforming our \nUser\n model from the previous section to the \nValidatable\n protocol. \n\n\n\n\nNote\n\n\nThis assumes \nUser\n already conforms to \nReflectable\n (added by default when using one of Fluent's \nModel\n protocols). If not, you will need to add conformance to \nReflectable\n manually.\n\n\n\n\nextension\n \nUser\n:\n \nValidatable\n \n{\n\n \n/// See `Validatable`.\n\n \nstatic\n \nfunc\n \nvalidations\n()\n \n-\n \nValidations\nUser\n \n{\n\n \n// define validations\n\n \n}\n\n\n}\n\n\n\nlet\n \nuser\n \n=\n \nUser\n(...)\n\n\n// since User conforms to Validatable, we get a new method validate()\n\n\n// that throws an error if any validations fail\n\n\ntry\n \nuser\n.\nvalidate\n()\n \n\n\n\n\n\nThis is the basic structure of \nValidatable\n conformance. Let's take a look at how we can implement the static \nvalidations()\n method.\n\n\nValidations\n\n\nFirst let's start by verifying that the name is at least 3 characters long.\n\n\nextension\n \nUser\n:\n \nValidatable\n \n{\n\n \n/// See `Validatable`.\n\n \nstatic\n \nfunc\n \nvalidations\n()\n \nthrows\n \n-\n \nValidations\nUser\n \n{\n\n \nvar\n \nvalidations\n \n=\n \nValidations\n(\nUser\n.\nself\n)\n\n \ntry\n \nvalidations\n.\nadd\n(\n\\\n.\nname\n,\n \n.\ncount\n(\n3.\n..))\n\n \nreturn\n \nvalidations\n\n \n}\n\n\n}\n\n\n\n\n\n\nThe \ncount(...)\n validation accepts Swift \nRange\n notation and will validate that a collection's count is within that range. By only placing a value on the left side of \n...\n, we only set a minimum range.\n\n\nTake a look at all of the available validators \nhere\n.\n\n\nOperators\n\n\nValidating that the name is three or more characters is great, but we also want to make sure that the name is alphanumeric characters only. We can do this by combining multiple validators using \n.\n\n\ntry\n \nvalidations\n.\nadd\n(\n\\\n.\nname\n,\n \n.\ncount\n(\n3.\n..)\n \n \n.\nalphanumeric\n)\n\n\n\n\n\n\nNow our name will only be considered valid if it is three or more characters \nand\n alphanumeric. Take a look at all of the available operators \nhere\n.\n\n\nNil\n\n\nYou may want to run validations on optionals only if a value is present. The \n and \n||\n operators have special overloads that help you do this. \n\n\ntry\n \nvalidations\n.\nadd\n(\n\\\n.\nemail\n,\n \n.\nemail\n \n||\n \n.\nnil\n)\n\n\n\n\n\n\nThe \nnil\n validator checks if a \nT?\n optional value is \nnil\n.\n\n\nThe \nemail\n validator checks if a \nString\n is a valid email address. However, the property on our \nUser\n is a \nString?\n. This means the email validator cannot be used directly with the property.\n\n\nWe can combine these two operators using \n||\n to express the validation we want: validate the email is correctly formatted if it is not nil.\n\n\nValidate\n\n\nLet's finish up the rest of our validations using our new knowledge.\n\n\nextension\n \nUser\n:\n \nValidatable\n \n{\n\n \n/// See `Validatable`.\n\n \nstatic\n \nfunc\n \nvalidations\n()\n \nthrows\n \n-\n \nValidations\nUser\n \n{\n\n \nvar\n \nvalidations\n \n=\n \nValidations\n(\nUser\n.\nself\n)\n\n \ntry\n \nvalidations\n.\nadd\n(\n\\\n.\nname\n,\n \n.\nalphanumeric\n \n \n.\ncount\n(\n3.\n..))\n\n \ntry\n \nvalidations\n.\nadd\n(\n\\\n.\nage\n,\n \n.\nrange\n(\n18.\n..))\n\n \ntry\n \nvalidations\n.\nadd\n(\n\\\n.\nemail\n,\n \n.\nemail\n \n||\n \n.\nnil\n)\n\n \nreturn\n \nvalidations\n\n \n}\n\n\n}\n\n\n\n\n\n\nNow let's try out validating our model.\n\n\nrouter\n.\npost\n(\nUser\n.\nself\n,\n \nat\n:\n \nusers\n)\n \n{\n \nreq\n,\n \nuser\n \n-\n \nUser\n \nin\n\n \ntry\n \nuser\n.\nvalidate\n()\n\n \nreturn\n \nuser\n\n\n}\n\n\n\n\n\n\nWhen you query that route, you should see that errors are thrown if the data does not meet your validations. If the data is correct, your user model is returned successfully.\n\n\nCongratulations on setting up your first \nValidatable\n model! Check out the \nAPI docs\n for more information and code samples.",
|
|
"title": "Overview"
|
|
},
|
|
{
|
|
"location": "/validation/overview/#validation-overview",
|
|
"text": "Validation is a framework for validating data sent to your application. It can help validate things like names, emails and more. It is also extensible, allowing you to easily create custom validators.",
|
|
"title": "Validation Overview"
|
|
},
|
|
{
|
|
"location": "/validation/overview/#swift-codable",
|
|
"text": "Swift's strong type system and Codable take care of most of the basic validation that web apps need to do. struct User : Codable { \n var id : UUID ? \n var name : String \n var age : Int \n var email : String ? } For example, when you decode the above User model, Swift will automatically ensure the following: id is a valid UUID or is nil . name is a valid String and is not nil . age is a valid Int and is not nil . email is a valid String or is nil . This is a great first step, but there is still room for improvement here. Here are some examples of things Swift and Codable would not mind, but are not ideal: name is empty string \"\" name contains non-alphanumeric characters age is a negative number -42 email is not correctly formatted test@@vapor.codes Luckily the Validation package can help.",
|
|
"title": "Swift & Codable"
|
|
},
|
|
{
|
|
"location": "/validation/overview/#validatable",
|
|
"text": "Let's take a look at how the Validation package can help you validate incoming data. We'll start by conforming our User model from the previous section to the Validatable protocol. Note This assumes User already conforms to Reflectable (added by default when using one of Fluent's Model protocols). If not, you will need to add conformance to Reflectable manually. extension User : Validatable { \n /// See `Validatable`. \n static func validations () - Validations User { \n // define validations \n } } let user = User (...) // since User conforms to Validatable, we get a new method validate() // that throws an error if any validations fail try user . validate () This is the basic structure of Validatable conformance. Let's take a look at how we can implement the static validations() method.",
|
|
"title": "Validatable"
|
|
},
|
|
{
|
|
"location": "/validation/overview/#validations",
|
|
"text": "First let's start by verifying that the name is at least 3 characters long. extension User : Validatable { \n /// See `Validatable`. \n static func validations () throws - Validations User { \n var validations = Validations ( User . self ) \n try validations . add ( \\ . name , . count ( 3. ..)) \n return validations \n } } The count(...) validation accepts Swift Range notation and will validate that a collection's count is within that range. By only placing a value on the left side of ... , we only set a minimum range. Take a look at all of the available validators here .",
|
|
"title": "Validations"
|
|
},
|
|
{
|
|
"location": "/validation/overview/#operators",
|
|
"text": "Validating that the name is three or more characters is great, but we also want to make sure that the name is alphanumeric characters only. We can do this by combining multiple validators using . try validations . add ( \\ . name , . count ( 3. ..) . alphanumeric ) Now our name will only be considered valid if it is three or more characters and alphanumeric. Take a look at all of the available operators here .",
|
|
"title": "Operators"
|
|
},
|
|
{
|
|
"location": "/validation/overview/#nil",
|
|
"text": "You may want to run validations on optionals only if a value is present. The and || operators have special overloads that help you do this. try validations . add ( \\ . email , . email || . nil ) The nil validator checks if a T? optional value is nil . The email validator checks if a String is a valid email address. However, the property on our User is a String? . This means the email validator cannot be used directly with the property. We can combine these two operators using || to express the validation we want: validate the email is correctly formatted if it is not nil.",
|
|
"title": "Nil"
|
|
},
|
|
{
|
|
"location": "/validation/overview/#validate",
|
|
"text": "Let's finish up the rest of our validations using our new knowledge. extension User : Validatable { \n /// See `Validatable`. \n static func validations () throws - Validations User { \n var validations = Validations ( User . self ) \n try validations . add ( \\ . name , . alphanumeric . count ( 3. ..)) \n try validations . add ( \\ . age , . range ( 18. ..)) \n try validations . add ( \\ . email , . email || . nil ) \n return validations \n } } Now let's try out validating our model. router . post ( User . self , at : users ) { req , user - User in \n try user . validate () \n return user } When you query that route, you should see that errors are thrown if the data does not meet your validations. If the data is correct, your user model is returned successfully. Congratulations on setting up your first Validatable model! Check out the API docs for more information and code samples.",
|
|
"title": "Validate"
|
|
},
|
|
{
|
|
"location": "/vapor/getting-started/",
|
|
"text": "Getting Started with Vapor\n\n\nCheck out the main \nGetting Started\n guide which covers Vapor specifically. This page is here mostly for consistency with the rest of the packages.\n\n\nMore in-depth information on the APIs included in Vapor, see the sub-sections to the left.\n\n\nPackage\n\n\nIf you don't want to use one of Vapor's templates to get started, you can always include the framework manually.\n\n\n// swift-tools-version:4.0\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/vapor.git\n,\n \nfrom\n:\n \n3.0.0\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nVapor\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport Vapor\n to access the APIs.\n\n\nAPI Docs\n\n\nThe rest of this guide will give you an overview of what is available in the Vapor package. As always, feel free to visit the \nAPI docs\n for more in-depth information.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/vapor/getting-started/#getting-started-with-vapor",
|
|
"text": "Check out the main Getting Started guide which covers Vapor specifically. This page is here mostly for consistency with the rest of the packages. More in-depth information on the APIs included in Vapor, see the sub-sections to the left.",
|
|
"title": "Getting Started with Vapor"
|
|
},
|
|
{
|
|
"location": "/vapor/getting-started/#package",
|
|
"text": "If you don't want to use one of Vapor's templates to get started, you can always include the framework manually. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . package ( url : https://github.com/vapor/vapor.git , from : 3.0.0 ), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ Vapor , ... ]) \n ] ) Use import Vapor to access the APIs.",
|
|
"title": "Package"
|
|
},
|
|
{
|
|
"location": "/vapor/getting-started/#api-docs",
|
|
"text": "The rest of this guide will give you an overview of what is available in the Vapor package. As always, feel free to visit the API docs for more in-depth information.",
|
|
"title": "API Docs"
|
|
},
|
|
{
|
|
"location": "/vapor/client/",
|
|
"text": "Using Client\n\n\nClient\n is a convenience wrapper around the lower level \nHTTP \n Client\n. It automatically parses things like hostname and port from URIs and helps you encode and decode \nContent\n.\n\n\nlet\n \nres\n \n=\n \ntry\n \nreq\n.\nclient\n().\nget\n(\nhttp://vapor.codes\n)\n\n\nprint\n(\nres\n)\n \n// Future\nResponse\n\n\n\n\n\n\nContainer\n\n\nThe first thing you will need is a service \nContainer\n to create your client.\n\n\nIf you are making this external API request as the result of an incoming request to your server, you should use the \nRequest\n container to create a client. This is most often the case. \n\n\nIf you need a client during boot, use the \nApplication\n or if you are in a \nCommand\n use the command context's container.\n\n\nOnce you have a \nContainer\n, use the \nclient()\n method to create a \nClient\n.\n\n\n// Creates a generic Client\n\n\nlet\n \nclient\n \n=\n \ntry\n \ncontainer\n.\nclient\n()\n\n\n\n\n\n\nSend\n\n\nOnce you have a \nClient\n, you can use the \nsend(...)\n method to send a \nRequest\n. Note that the request URI must include a scheme and hostname.\n\n\nlet\n \nreq\n:\n \nRequest\n \n...\n\n\nlet\n \nres\n \n=\n \ntry\n \nclient\n.\nsend\n(\nreq\n)\n\n\nprint\n(\nres\n)\n \n// Future\nResponse\n\n\n\n\n\n\nYou can also use the convenience methods like \nget(...)\n, \npost(...)\n, etc.\n\n\nlet\n \nuser\n:\n \nUser\n \n...\n\n\nlet\n \nres\n \n=\n \ntry\n \nclient\n.\npost\n(\nhttp://api.vapor.codes/users\n)\n \n{\n \npost\n \nin\n\n \ntry\n \npost\n.\ncontent\n.\nencode\n(\nuser\n)\n\n\n}\n\n\nprint\n(\nres\n)\n \n// Future\nResponse\n\n\n\n\n\n\nSee \nContent\n for more information on encoding and decoding content to messages.",
|
|
"title": "Client"
|
|
},
|
|
{
|
|
"location": "/vapor/client/#using-client",
|
|
"text": "Client is a convenience wrapper around the lower level HTTP Client . It automatically parses things like hostname and port from URIs and helps you encode and decode Content . let res = try req . client (). get ( http://vapor.codes ) print ( res ) // Future Response",
|
|
"title": "Using Client"
|
|
},
|
|
{
|
|
"location": "/vapor/client/#container",
|
|
"text": "The first thing you will need is a service Container to create your client. If you are making this external API request as the result of an incoming request to your server, you should use the Request container to create a client. This is most often the case. If you need a client during boot, use the Application or if you are in a Command use the command context's container. Once you have a Container , use the client() method to create a Client . // Creates a generic Client let client = try container . client ()",
|
|
"title": "Container"
|
|
},
|
|
{
|
|
"location": "/vapor/client/#send",
|
|
"text": "Once you have a Client , you can use the send(...) method to send a Request . Note that the request URI must include a scheme and hostname. let req : Request ... let res = try client . send ( req ) print ( res ) // Future Response You can also use the convenience methods like get(...) , post(...) , etc. let user : User ... let res = try client . post ( http://api.vapor.codes/users ) { post in \n try post . content . encode ( user ) } print ( res ) // Future Response See Content for more information on encoding and decoding content to messages.",
|
|
"title": "Send"
|
|
},
|
|
{
|
|
"location": "/vapor/content/",
|
|
"text": "Using Content\n\n\nIn Vapor 3, all content types (JSON, protobuf, \nURLEncodedForm\n, \nMultipart\n, etc) are treated the same. All you need to parse and serialize content is a \nCodable\n class or struct.\n\n\nFor this introduction, we will use mostly JSON as an example. But keep in mind the API is the same for any supported content type.\n\n\nServer\n\n\nThis first section will go over decoding and encoding messages sent between your server and connected clients. See the \nclient\n section for encoding and decoding content in messages sent to external APIs.\n\n\nRequest\n\n\nLet's take a look at how you would parse the following HTTP request sent to your server.\n\n\nPOST\n \n/login\n \nHTTP\n/\n1.1\n\n\nContent-Type\n:\n \napplication/json\n\n\n\n{\n\n \nemail\n:\n \nuser@vapor.codes\n,\n\n \npassword\n:\n \ndon\nt look!\n\n\n}\n\n\n\n\n\n\nFirst, create a struct or class that represents the data you expect. \n\n\nimport\n \nVapor\n\n\n\nstruct\n \nLoginRequest\n:\n \nContent\n \n{\n\n \nvar\n \nemail\n:\n \nString\n\n \nvar\n \npassword\n:\n \nString\n\n\n}\n\n\n\n\n\n\nNotice the key names exactly match the keys in the request data. The expected data types also match. Next conform this struct or class to \nContent\n.\n\n\nDecode\n\n\nNow we are ready to decode that HTTP request. Every \nRequest\n has a \nContentContainer\n that we can use to decode content from the message's body.\n\n\nrouter\n.\npost\n(\nlogin\n)\n \n{\n \nreq\n \n-\n \nFuture\nHTTPStatus\n \nin\n\n \nreturn\n \nreq\n.\ncontent\n.\ndecode\n(\nLoginRequest\n.\nself\n).\nmap\n \n{\n \nloginRequest\n \nin\n\n \nprint\n(\nloginRequest\n.\nemail\n)\n \n// user@vapor.codes\n\n \nprint\n(\nloginRequest\n.\npassword\n)\n \n// don\nt look!\n\n \nreturn\n \nHTTPStatus\n.\nok\n\n \n}\n\n\n}\n\n\n\n\n\n\nWe use \n.map(to:)\n here since \ndecode(...)\n returns a \nfuture\n. \n\n\n\n\nNote\n\n\nDecoding content from requests is asynchronous because HTTP allows bodies to be split into multiple parts using chunked transfer encoding. \n\n\n\n\nRouter\n\n\nTo help make decoding content from incoming requests easier, Vapor offers a few extensions on \nRouter\n to do this automatically.\n\n\nrouter\n.\npost\n(\nLoginRequest\n.\nself\n,\n \nat\n:\n \nlogin\n)\n \n{\n \nreq\n,\n \nloginRequest\n \nin\n\n \nprint\n(\nloginRequest\n.\nemail\n)\n \n// user@vapor.codes\n\n \nprint\n(\nloginRequest\n.\npassword\n)\n \n// don\nt look!\n\n \nreturn\n \nHTTPStatus\n.\nok\n\n\n}\n\n\n\n\n\n\nDetect Type\n\n\nSince the HTTP request in this example declared JSON as its content type, Vapor knows to use a JSON decoder automatically. This same method would work just as well for the following request.\n\n\nPOST\n \n/login\n \nHTTP\n/\n1.1\n\n\nContent-Type\n:\n \napplication/x-www-form-urlencoded\n\n\nemail=user@vapor.codes\ndon\nt+look!\n\n\n\n\n\nAll HTTP requests must include a content type to be valid. Because of this, Vapor will automatically choose an appropriate decoder or error if it encounters an unknown media type.\n\n\n\n\nTip\n\n\nYou can \nconfigure\n the default encoders and decoders Vapor uses.\n\n\n\n\nCustom\n\n\nYou can always override Vapor's default decoder and pass in a custom one if you want.\n\n\nlet\n \nuser\n \n=\n \ntry\n \nreq\n.\ncontent\n.\ndecode\n(\nUser\n.\nself\n,\n \nusing\n:\n \nJSONDecoder\n())\n\n\nprint\n(\nuser\n)\n \n// Future\nUser\n\n\n\n\n\n\nResponse\n\n\nLet's take a look at how you would create the following HTTP response from your server.\n\n\nHTTP\n/\n1.1\n \n200\n \nOK\n\n\nContent-Type\n:\n \napplication/json\n\n\n\n{\n\n \nname\n:\n \nVapor User\n,\n\n \nemail\n:\n \nuser@vapor.codes\n\n\n}\n\n\n\n\n\n\nJust like decoding, first create a struct or class that represents the data that you are expecting.\n\n\nimport\n \nVapor\n\n\n\nstruct\n \nUser\n:\n \nContent\n \n{\n\n \nvar\n \nname\n:\n \nString\n\n \nvar\n \nemail\n:\n \nString\n\n\n}\n\n\n\n\n\n\nThen just conform this struct or class to \nContent\n. \n\n\nEncode\n\n\nNow we are ready to encode that HTTP response.\n\n\nrouter\n.\nget\n(\nuser\n)\n \n{\n \nreq\n \n-\n \nUser\n \nin\n\n \nreturn\n \nUser\n(\nname\n:\n \nVapor User\n,\n \nemail\n:\n \nuser@vapor.codes\n)\n\n\n}\n\n\n\n\n\n\nThis will create a default \nResponse\n with \n200 OK\n status code and minimal headers. You can customize the response using a convenience \nencode(...)\n method.\n\n\nrouter\n.\nget\n(\nuser\n)\n \n{\n \nreq\n \n-\n \nFuture\nResponse\n \nin\n\n \nreturn\n \nUser\n(\nname\n:\n \nVapor User\n,\n \nemail\n:\n \nuser@vapor.codes\n)\n\n \n.\nencode\n(\nstatus\n:\n \n.\ncreated\n)\n\n\n}\n\n\n\n\n\n\nOverride Type\n\n\nContent will automatically encode as JSON by default. You can always override which content type is used\nusing the \nas:\n parameter.\n\n\ntry\n \nres\n.\ncontent\n.\nencode\n(\nuser\n,\n \nas\n:\n \n.\nurlEncodedForm\n)\n\n\n\n\n\n\nYou can also change the default media type for any class or struct.\n\n\nstruct\n \nUser\n:\n \nContent\n \n{\n\n \n/// See `Content`.\n\n \nstatic\n \nlet\n \ndefaultContentType\n:\n \nMediaType\n \n=\n \n.\nurlEncodedForm\n\n\n \n...\n\n\n}\n\n\n\n\n\n\nClient\n\n\nEncoding content to HTTP requests sent by \nClient\ns is similar to encoding HTTP responses returned by your server. \n\n\nRequest\n\n\nLet's take a look at how we can encode the following request.\n\n\nPOST\n \n/login\n \nHTTP\n/\n1.1\n\n\nHost\n:\n \napi.vapor.codes\n\n\nContent-Type\n:\n \napplication/json\n\n\n\n{\n\n \nemail\n:\n \nuser@vapor.codes\n,\n\n \npassword\n:\n \ndon\nt look!\n\n\n}\n\n\n\n\n\n\nEncode\n\n\nFirst, create a struct or class that represents the data you expect.\n\n\nimport\n \nVapor\n\n\n\nstruct\n \nLoginRequest\n:\n \nContent\n \n{\n\n \nvar\n \nemail\n:\n \nString\n\n \nvar\n \npassword\n:\n \nString\n\n\n}\n\n\n\n\n\n\nNow we are ready to make our request. Let's assume we are making this request inside of a route closure, so we will use the \nincoming\n request as our container. \n\n\nlet\n \nloginRequest\n \n=\n \nLoginRequest\n(\nemail\n:\n \nuser@vapor.codes\n,\n \npassword\n:\n \ndon\nt look!\n)\n\n\nlet\n \nres\n \n=\n \ntry\n \nreq\n.\nclient\n().\npost\n(\nhttps://api.vapor.codes/login\n)\n \n{\n \nloginReq\n \nin\n\n \n// encode the loginRequest before sending\n\n \ntry\n \nloginReq\n.\ncontent\n.\nencode\n(\nloginRequest\n)\n\n\n}\n\n\nprint\n(\nres\n)\n \n// Future\nResponse\n\n\n\n\n\n\nResponse\n\n\nContinuing from our example in the encode section, let's see how we would decode content from the client's response.\n\n\nHTTP\n/\n1.1\n \n200\n \nOK\n\n\nContent-Type\n:\n \napplication/json\n\n\n\n{\n\n \nname\n:\n \nVapor User\n,\n\n \nemail\n:\n \nuser@vapor.codes\n\n\n}\n\n\n\n\n\n\nFirst of course we must create a struct or class to represent the data.\n\n\nimport\n \nVapor\n\n\n\nstruct\n \nUser\n:\n \nContent\n \n{\n\n \nvar\n \nname\n:\n \nString\n\n \nvar\n \nemail\n:\n \nString\n\n\n}\n\n\n\n\n\n\nDecode\n\n\nNow we are ready to decode the client response.\n\n\nlet\n \nres\n:\n \nFuture\nResponse\n \n// from the Client\n\n\n\nlet\n \nuser\n \n=\n \nres\n.\nflatMap\n \n{\n \ntry\n \n$0\n.\ncontent\n.\ndecode\n(\nUser\n.\nself\n)\n \n}\n\n\nprint\n(\nuser\n)\n \n// Future\nUser\n\n\n\n\n\n\nExample\n\n\nLet's now take a look at our complete \nClient\n request that both encodes and decodes content.\n\n\n// Create the LoginRequest data\n\n\nlet\n \nloginRequest\n \n=\n \nLoginRequest\n(\nemail\n:\n \nuser@vapor.codes\n,\n \npassword\n:\n \ndon\nt look!\n)\n\n\n// POST /login\n\n\nlet\n \nuser\n \n=\n \ntry\n \nreq\n.\nclient\n().\npost\n(\nhttps://api.vapor.codes/login\n)\n \n{\n \nloginReq\n \nin\n \n \n// Encode Content before Request is sent\n\n \nreturn\n \ntry\n \nloginReq\n.\ncontent\n.\nencode\n(\nloginRequest\n)\n \n\n}.\nflatMap\n \n{\n \nloginRes\n \nin\n\n \n// Decode Content after Response is received\n\n \nreturn\n \ntry\n \nloginRes\n.\ncontent\n.\ndecode\n(\nUser\n.\nself\n)\n \n\n}\n\n\nprint\n(\nuser\n)\n \n// Future\nUser\n\n\n\n\n\n\nQuery String\n\n\nURL-Encoded Form data can be encoded and decoded from an HTTP request's URI query string just like content. All you need is a class or struct that conforms to \nContent\n. In these examples, we will be using the following struct.\n\n\nstruct\n \nFlags\n:\n \nContent\n \n{\n\n \nvar\n \nsearch\n:\n \nString\n?\n\n \nvar\n \nisAdmin\n:\n \nBool\n?\n\n\n}\n\n\n\n\n\n\nDecode\n\n\nAll \nRequest\ns have a \nQueryContainer\n that you can use to decode the query string.\n\n\nlet\n \nflags\n \n=\n \ntry\n \nreq\n.\nquery\n.\ndecode\n(\nFlags\n.\nself\n)\n\n\nprint\n(\nflags\n)\n \n// Flags\n\n\n\n\n\n\nEncode\n\n\nYou can also encode content. This is useful for encoding query strings when using \nClient\n.\n\n\nlet\n \nflags\n:\n \nFlags\n \n...\n\n\ntry\n \nreq\n.\nquery\n.\nencode\n(\nflags\n)\n\n\n\n\n\n\nDynamic Properties\n\n\nOne of the most frequently asked questions regarding \nContent\n is:\n\n\n\n\nHow do I add a property to just this response?\n\n\n\n\nThe way Vapor 3 handles \nContent\n is based entirely on \nCodable\n. At no point (that is publically accessible) is your data in an arbitrary data structure like \n[String: Any]\n that you can mutate at will. Because of this, all data structures that your app accepts and returns \nmust\n be statically defined.\n\n\nLet's take a look at a common scenario to better understand this. Very often when you are creating a user, there are a couple different data formats required:\n\n\n\n\ncreate: password should be supplied twice to check values match\n\n\ninternal: you should store a hash not the plaintext password\n\n\npublic: when listing users, the password hash should not be included\n\n\n\n\nTo do this, you should create three types.\n\n\n// Data required to create a user\n\n\nstruct\n \nUserCreate\n:\n \nContent\n \n{\n\n \nvar\n \nemail\n:\n \nString\n\n \nvar\n \npassword\n:\n \nString\n\n \nvar\n \npasswordCheck\n:\n \nString\n\n\n}\n\n\n\n// Our internal User representation\n\n\nstruct\n \nUser\n:\n \nModel\n \n{\n\n \nvar\n \nid\n:\n \nInt\n?\n\n \nvar\n \nemail\n:\n \nString\n\n \nvar\n \npasswordHash\n:\n \nData\n\n\n}\n\n\n\n// Public user representation\n\n\nstruct\n \nPublicUser\n:\n \nContent\n \n{\n\n \nvar\n \nid\n:\n \nInt\n\n \nvar\n \nemail\n:\n \nString\n\n\n}\n\n\n\n// Create a router for POST /users\n\n\nrouter\n.\npost\n(\nUserCreate\n.\nself\n,\n \nat\n:\n \nusers\n)\n \n{\n \nreq\n,\n \nuserCreate\n \n-\n \nPublicUser\n \nin\n\n \nguard\n \nuserCreate\n.\npassword\n \n==\n \npasswordCheck\n \nelse\n \n{\n \n/* some error */\n \n}\n\n \nlet\n \nhasher\n \n=\n \ntry\n \nreq\n.\nmake\n(\n/* some hasher */\n)\n\n \nlet\n \nuser\n \n=\n \ntry\n \nUser\n(\n\n \nemail\n:\n \nuserCreate\n.\nemail\n,\n \n \npasswordHash\n:\n \nhasher\n.\nhash\n(\nuserCreate\n.\npassword\n)\n\n \n)\n\n \n// save user\n\n \nreturn\n \ntry\n \nPublicUser\n(\nid\n:\n \nuser\n.\nrequireID\n(),\n \nemail\n:\n \nuser\n.\nemail\n)\n\n\n}\n\n\n\n\n\n\nFor other methods such as \nPATCH\n and \nPUT\n, you may want to create additional types to supports the unique semantics.\n\n\nBenefits\n\n\nThis method may seem a bit verbose at first when compared to dynamic solutions, but it has a number of key advantages:\n\n\n\n\nStatically Typed\n: Very little validation is needed on top of what Swift and Codable do automatically.\n\n\nReadability\n: No need for Strings and optional chaining when working with Swift types.\n\n\nMaintainable\n: Large projects will appreciate having this information separated and clearly stated.\n\n\nShareable\n: Types defining what content your routes accept and return can be used to conform to specifications like OpenAPI or even be shared directly with clients written in Swift.\n\n\nPerformance\n: Working with native Swift types is much more performant than mutating \n[String: Any]\n dictionaries.\n\n\n\n\nJSON\n\n\nJSON is a very popular encoding format for APIs and the way in which dates, data, floats, etc are encoded is non-standard. Because of this, Vapor makes it easy to use custom \nJSONDecoder\ns when you interact with other APIs.\n\n\n// Conforms to Encodable\n\n\nlet\n \nuser\n:\n \nUser\n \n...\n \n\n// Encode JSON using custom date encoding strategy\n\n\ntry\n \nreq\n.\ncontent\n.\nencode\n(\njson\n:\n \nuser\n,\n \nusing\n:\n \n.\ncustom\n(\ndates\n:\n \n.\nmillisecondsSince1970\n))\n\n\n\n\n\n\nYou can also use this method for decoding.\n\n\n// Decode JSON using custom date encoding strategy\n\n\nlet\n \nuser\n \n=\n \ntry\n \nreq\n.\ncontent\n.\ndecode\n(\njson\n:\n \nUser\n.\nself\n,\n \nusing\n:\n \n.\ncustom\n(\ndates\n:\n \n.\nmillisecondsSince1970\n))\n\n\n\n\n\n\nIf you would like to set a custom JSON encoder or decoder globally, you can do so using \nconfiguration\n.\n\n\nConfigure\n\n\nUse \nContentConfig\n to register custom encoder/decoders for your application. These custom coders will be used anywhere you do \ncontent.encode\n/\ncontent.decode\n.\n\n\n/// Create default content config\n\n\nvar\n \ncontentConfig\n \n=\n \nContentConfig\n.\ndefault\n()\n\n\n\n/// Create custom JSON encoder\n\n\nvar\n \njsonEncoder\n \n=\n \nJSONEncoder\n()\n\n\njsonEncoder\n.\ndateEncodingStrategy\n \n=\n \n.\nmillisecondsSince1970\n\n\n\n/// Register JSON encoder and content config\n\n\ncontentConfig\n.\nuse\n(\nencoder\n:\n \njsonEncoder\n,\n \nfor\n:\n \n.\njson\n)\n\n\nservices\n.\nregister\n(\ncontentConfig\n)",
|
|
"title": "Content"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#using-content",
|
|
"text": "In Vapor 3, all content types (JSON, protobuf, URLEncodedForm , Multipart , etc) are treated the same. All you need to parse and serialize content is a Codable class or struct. For this introduction, we will use mostly JSON as an example. But keep in mind the API is the same for any supported content type.",
|
|
"title": "Using Content"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#server",
|
|
"text": "This first section will go over decoding and encoding messages sent between your server and connected clients. See the client section for encoding and decoding content in messages sent to external APIs.",
|
|
"title": "Server"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#request",
|
|
"text": "Let's take a look at how you would parse the following HTTP request sent to your server. POST /login HTTP / 1.1 Content-Type : application/json { \n email : user@vapor.codes , \n password : don t look! } First, create a struct or class that represents the data you expect. import Vapor struct LoginRequest : Content { \n var email : String \n var password : String } Notice the key names exactly match the keys in the request data. The expected data types also match. Next conform this struct or class to Content .",
|
|
"title": "Request"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#decode",
|
|
"text": "Now we are ready to decode that HTTP request. Every Request has a ContentContainer that we can use to decode content from the message's body. router . post ( login ) { req - Future HTTPStatus in \n return req . content . decode ( LoginRequest . self ). map { loginRequest in \n print ( loginRequest . email ) // user@vapor.codes \n print ( loginRequest . password ) // don t look! \n return HTTPStatus . ok \n } } We use .map(to:) here since decode(...) returns a future . Note Decoding content from requests is asynchronous because HTTP allows bodies to be split into multiple parts using chunked transfer encoding.",
|
|
"title": "Decode"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#router",
|
|
"text": "To help make decoding content from incoming requests easier, Vapor offers a few extensions on Router to do this automatically. router . post ( LoginRequest . self , at : login ) { req , loginRequest in \n print ( loginRequest . email ) // user@vapor.codes \n print ( loginRequest . password ) // don t look! \n return HTTPStatus . ok }",
|
|
"title": "Router"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#detect-type",
|
|
"text": "Since the HTTP request in this example declared JSON as its content type, Vapor knows to use a JSON decoder automatically. This same method would work just as well for the following request. POST /login HTTP / 1.1 Content-Type : application/x-www-form-urlencoded \n\nemail=user@vapor.codes don t+look! All HTTP requests must include a content type to be valid. Because of this, Vapor will automatically choose an appropriate decoder or error if it encounters an unknown media type. Tip You can configure the default encoders and decoders Vapor uses.",
|
|
"title": "Detect Type"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#custom",
|
|
"text": "You can always override Vapor's default decoder and pass in a custom one if you want. let user = try req . content . decode ( User . self , using : JSONDecoder ()) print ( user ) // Future User",
|
|
"title": "Custom"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#response",
|
|
"text": "Let's take a look at how you would create the following HTTP response from your server. HTTP / 1.1 200 OK Content-Type : application/json { \n name : Vapor User , \n email : user@vapor.codes } Just like decoding, first create a struct or class that represents the data that you are expecting. import Vapor struct User : Content { \n var name : String \n var email : String } Then just conform this struct or class to Content .",
|
|
"title": "Response"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#encode",
|
|
"text": "Now we are ready to encode that HTTP response. router . get ( user ) { req - User in \n return User ( name : Vapor User , email : user@vapor.codes ) } This will create a default Response with 200 OK status code and minimal headers. You can customize the response using a convenience encode(...) method. router . get ( user ) { req - Future Response in \n return User ( name : Vapor User , email : user@vapor.codes ) \n . encode ( status : . created ) }",
|
|
"title": "Encode"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#override-type",
|
|
"text": "Content will automatically encode as JSON by default. You can always override which content type is used\nusing the as: parameter. try res . content . encode ( user , as : . urlEncodedForm ) You can also change the default media type for any class or struct. struct User : Content { \n /// See `Content`. \n static let defaultContentType : MediaType = . urlEncodedForm \n\n ... }",
|
|
"title": "Override Type"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#client",
|
|
"text": "Encoding content to HTTP requests sent by Client s is similar to encoding HTTP responses returned by your server.",
|
|
"title": "Client"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#request_1",
|
|
"text": "Let's take a look at how we can encode the following request. POST /login HTTP / 1.1 Host : api.vapor.codes Content-Type : application/json { \n email : user@vapor.codes , \n password : don t look! }",
|
|
"title": "Request"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#encode_1",
|
|
"text": "First, create a struct or class that represents the data you expect. import Vapor struct LoginRequest : Content { \n var email : String \n var password : String } Now we are ready to make our request. Let's assume we are making this request inside of a route closure, so we will use the incoming request as our container. let loginRequest = LoginRequest ( email : user@vapor.codes , password : don t look! ) let res = try req . client (). post ( https://api.vapor.codes/login ) { loginReq in \n // encode the loginRequest before sending \n try loginReq . content . encode ( loginRequest ) } print ( res ) // Future Response",
|
|
"title": "Encode"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#response_1",
|
|
"text": "Continuing from our example in the encode section, let's see how we would decode content from the client's response. HTTP / 1.1 200 OK Content-Type : application/json { \n name : Vapor User , \n email : user@vapor.codes } First of course we must create a struct or class to represent the data. import Vapor struct User : Content { \n var name : String \n var email : String }",
|
|
"title": "Response"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#decode_1",
|
|
"text": "Now we are ready to decode the client response. let res : Future Response // from the Client let user = res . flatMap { try $0 . content . decode ( User . self ) } print ( user ) // Future User",
|
|
"title": "Decode"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#example",
|
|
"text": "Let's now take a look at our complete Client request that both encodes and decodes content. // Create the LoginRequest data let loginRequest = LoginRequest ( email : user@vapor.codes , password : don t look! ) // POST /login let user = try req . client (). post ( https://api.vapor.codes/login ) { loginReq in \n // Encode Content before Request is sent \n return try loginReq . content . encode ( loginRequest ) }. flatMap { loginRes in \n // Decode Content after Response is received \n return try loginRes . content . decode ( User . self ) } print ( user ) // Future User",
|
|
"title": "Example"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#query-string",
|
|
"text": "URL-Encoded Form data can be encoded and decoded from an HTTP request's URI query string just like content. All you need is a class or struct that conforms to Content . In these examples, we will be using the following struct. struct Flags : Content { \n var search : String ? \n var isAdmin : Bool ? }",
|
|
"title": "Query String"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#decode_2",
|
|
"text": "All Request s have a QueryContainer that you can use to decode the query string. let flags = try req . query . decode ( Flags . self ) print ( flags ) // Flags",
|
|
"title": "Decode"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#encode_2",
|
|
"text": "You can also encode content. This is useful for encoding query strings when using Client . let flags : Flags ... try req . query . encode ( flags )",
|
|
"title": "Encode"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#dynamic-properties",
|
|
"text": "One of the most frequently asked questions regarding Content is: How do I add a property to just this response? The way Vapor 3 handles Content is based entirely on Codable . At no point (that is publically accessible) is your data in an arbitrary data structure like [String: Any] that you can mutate at will. Because of this, all data structures that your app accepts and returns must be statically defined. Let's take a look at a common scenario to better understand this. Very often when you are creating a user, there are a couple different data formats required: create: password should be supplied twice to check values match internal: you should store a hash not the plaintext password public: when listing users, the password hash should not be included To do this, you should create three types. // Data required to create a user struct UserCreate : Content { \n var email : String \n var password : String \n var passwordCheck : String } // Our internal User representation struct User : Model { \n var id : Int ? \n var email : String \n var passwordHash : Data } // Public user representation struct PublicUser : Content { \n var id : Int \n var email : String } // Create a router for POST /users router . post ( UserCreate . self , at : users ) { req , userCreate - PublicUser in \n guard userCreate . password == passwordCheck else { /* some error */ } \n let hasher = try req . make ( /* some hasher */ ) \n let user = try User ( \n email : userCreate . email , \n passwordHash : hasher . hash ( userCreate . password ) \n ) \n // save user \n return try PublicUser ( id : user . requireID (), email : user . email ) } For other methods such as PATCH and PUT , you may want to create additional types to supports the unique semantics.",
|
|
"title": "Dynamic Properties"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#benefits",
|
|
"text": "This method may seem a bit verbose at first when compared to dynamic solutions, but it has a number of key advantages: Statically Typed : Very little validation is needed on top of what Swift and Codable do automatically. Readability : No need for Strings and optional chaining when working with Swift types. Maintainable : Large projects will appreciate having this information separated and clearly stated. Shareable : Types defining what content your routes accept and return can be used to conform to specifications like OpenAPI or even be shared directly with clients written in Swift. Performance : Working with native Swift types is much more performant than mutating [String: Any] dictionaries.",
|
|
"title": "Benefits"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#json",
|
|
"text": "JSON is a very popular encoding format for APIs and the way in which dates, data, floats, etc are encoded is non-standard. Because of this, Vapor makes it easy to use custom JSONDecoder s when you interact with other APIs. // Conforms to Encodable let user : User ... // Encode JSON using custom date encoding strategy try req . content . encode ( json : user , using : . custom ( dates : . millisecondsSince1970 )) You can also use this method for decoding. // Decode JSON using custom date encoding strategy let user = try req . content . decode ( json : User . self , using : . custom ( dates : . millisecondsSince1970 )) If you would like to set a custom JSON encoder or decoder globally, you can do so using configuration .",
|
|
"title": "JSON"
|
|
},
|
|
{
|
|
"location": "/vapor/content/#configure",
|
|
"text": "Use ContentConfig to register custom encoder/decoders for your application. These custom coders will be used anywhere you do content.encode / content.decode . /// Create default content config var contentConfig = ContentConfig . default () /// Create custom JSON encoder var jsonEncoder = JSONEncoder () jsonEncoder . dateEncodingStrategy = . millisecondsSince1970 /// Register JSON encoder and content config contentConfig . use ( encoder : jsonEncoder , for : . json ) services . register ( contentConfig )",
|
|
"title": "Configure"
|
|
},
|
|
{
|
|
"location": "/vapor/sessions/",
|
|
"text": "Using Sessions\n\n\nThis guide will show you how to use sessions in Vapor to maintain state for a connected client.\n\n\nSessions work by creating unique identifiers for each new client and asking the client to supply this identifier with each request. When the next request is received, Vapor uses this unique identifier to restore the session data. This identifier could be transmitted in any format, but it is almost always done with cookies and that is how Vapor's sessions work. \n\n\nWhen a new client connects and session data is set, Vapor will return a \nSet-Cookie\n header. The client is then expected to re-supply the value with each request in a \nCookie\n header. All browsers do this automatically. If your ever decide to invalidate the session, Vapor will delete any related data and notify the client that their cookie is no longer valid.\n\n\nMiddleware\n\n\nThe first step to using sessions is enabling \nSessionsMiddleware\n. This can be done globally for the entire application or on a per-route basis.\n\n\nGlobally\n\n\nTo globally enable sessions, add the middleware to your \nMiddlewareConfig\n.\n\n\nvar\n \nmiddlewares\n \n=\n \nMiddlewareConfig\n.\ndefault\n()\n\n\nmiddlewares\n.\nuse\n(\nSessionsMiddleware\n.\nself\n)\n\n\nservices\n.\nregister\n(\nmiddlewares\n)\n\n\n\n\n\n\nThis is usually done in \nconfigure.swift\n.\n\n\nPer Route\n\n\nTo enable sessions for a group of routes, use the \ngrouped(...)\n methods on \nRouter\n.\n\n\n// create a grouped router at /sessions w/ sessions enabled\n\n\nlet\n \nsessions\n \n=\n \nrouter\n.\ngrouped\n(\nsessions\n).\ngrouped\n(\nSessionsMiddleware\n.\nself\n)\n\n\n\n// create a route at GET /sessions/foo\n\n\nsessions\n.\nget\n(\nfoo\n)\n \n{\n \nreq\n \nin\n\n \n// use sessions\n\n\n}\n\n\n\n\n\n\nSessions\n\n\nWhen \nSessionsMiddleware\n boots it will attempt to make a \nSessions\n and a \nSessionsConfig\n. Vapor will use an in-memory session by default. You can override both of these services by registering them in \nconfigure.swift\n.\n\n\nYou can use Fluent databases (like MySQL, PostgreSQL, etc) or caches like Redis to store your sessions. See the respective guides for more information.\n\n\nSession\n\n\nOnce you have \nSessionsMiddleware\n enabled, you can use \nreq.session()\n to access the session. Here is a simple example that does simple CRUD operations on a \n\"name\"\n value in the session.\n\n\n// create a route at GET /sessions/get\n\n\nsessions\n.\nget\n(\nget\n)\n \n{\n \nreq\n \n-\n \nString\n \nin\n\n \n// access \nname\n from session or return n/a\n\n \nreturn\n \ntry\n \nreq\n.\nsession\n()[\nname\n]\n \n??\n \nn/a\n\n\n}\n\n\n\n// create a route at GET /sessions/set/:name\n\n\nsessions\n.\nget\n(\nset\n,\n \nString\n.\nparameter\n)\n \n{\n \nreq\n \n-\n \nString\n \nin\n\n \n// get router param\n\n \nlet\n \nname\n \n=\n \ntry\n \nreq\n.\nparameters\n.\nnext\n(\nString\n.\nself\n)\n\n\n \n// set name to session at key \nname\n\n \ntry\n \nreq\n.\nsession\n()[\nname\n]\n \n=\n \nname\n\n\n \n// return the newly set name\n\n \nreturn\n \nname\n\n\n}\n\n\n\n// create a route at GET /sessions/del\n\n\nsessions\n.\nget\n(\ndel\n)\n \n{\n \nreq\n \n-\n \nString\n \nin\n\n \n// destroy the session\n\n \ntry\n \nreq\n.\ndestroySession\n()\n\n\n \n// signal success\n\n \nreturn\n \ndone\n\n\n}\n\n\n\n\n\n\nThat's it, congratulations on getting sessions working!",
|
|
"title": "Sessions"
|
|
},
|
|
{
|
|
"location": "/vapor/sessions/#using-sessions",
|
|
"text": "This guide will show you how to use sessions in Vapor to maintain state for a connected client. Sessions work by creating unique identifiers for each new client and asking the client to supply this identifier with each request. When the next request is received, Vapor uses this unique identifier to restore the session data. This identifier could be transmitted in any format, but it is almost always done with cookies and that is how Vapor's sessions work. When a new client connects and session data is set, Vapor will return a Set-Cookie header. The client is then expected to re-supply the value with each request in a Cookie header. All browsers do this automatically. If your ever decide to invalidate the session, Vapor will delete any related data and notify the client that their cookie is no longer valid.",
|
|
"title": "Using Sessions"
|
|
},
|
|
{
|
|
"location": "/vapor/sessions/#middleware",
|
|
"text": "The first step to using sessions is enabling SessionsMiddleware . This can be done globally for the entire application or on a per-route basis.",
|
|
"title": "Middleware"
|
|
},
|
|
{
|
|
"location": "/vapor/sessions/#globally",
|
|
"text": "To globally enable sessions, add the middleware to your MiddlewareConfig . var middlewares = MiddlewareConfig . default () middlewares . use ( SessionsMiddleware . self ) services . register ( middlewares ) This is usually done in configure.swift .",
|
|
"title": "Globally"
|
|
},
|
|
{
|
|
"location": "/vapor/sessions/#per-route",
|
|
"text": "To enable sessions for a group of routes, use the grouped(...) methods on Router . // create a grouped router at /sessions w/ sessions enabled let sessions = router . grouped ( sessions ). grouped ( SessionsMiddleware . self ) // create a route at GET /sessions/foo sessions . get ( foo ) { req in \n // use sessions }",
|
|
"title": "Per Route"
|
|
},
|
|
{
|
|
"location": "/vapor/sessions/#sessions",
|
|
"text": "When SessionsMiddleware boots it will attempt to make a Sessions and a SessionsConfig . Vapor will use an in-memory session by default. You can override both of these services by registering them in configure.swift . You can use Fluent databases (like MySQL, PostgreSQL, etc) or caches like Redis to store your sessions. See the respective guides for more information.",
|
|
"title": "Sessions"
|
|
},
|
|
{
|
|
"location": "/vapor/sessions/#session",
|
|
"text": "Once you have SessionsMiddleware enabled, you can use req.session() to access the session. Here is a simple example that does simple CRUD operations on a \"name\" value in the session. // create a route at GET /sessions/get sessions . get ( get ) { req - String in \n // access name from session or return n/a \n return try req . session ()[ name ] ?? n/a } // create a route at GET /sessions/set/:name sessions . get ( set , String . parameter ) { req - String in \n // get router param \n let name = try req . parameters . next ( String . self ) \n\n // set name to session at key name \n try req . session ()[ name ] = name \n\n // return the newly set name \n return name } // create a route at GET /sessions/del sessions . get ( del ) { req - String in \n // destroy the session \n try req . destroySession () \n\n // signal success \n return done } That's it, congratulations on getting sessions working!",
|
|
"title": "Session"
|
|
},
|
|
{
|
|
"location": "/vapor/websocket/",
|
|
"text": "Using WebSockets\n\n\nVapor includes convenience methods for working with the lower level WebSocket \nclient\n and \nserver\n. \n\n\nServer\n\n\nVapor's WebSocket server includes the ability to route incoming requests just like its HTTP server. \n\n\nWhen Vapor's main HTTP \nServer\n boots it will attempt to create a \nWebSocketServer\n. If one is registered, it will be added as an HTTP upgrade handler to the server. \n\n\nSo to create a WebSocket server, all you need to do is register one in \nconfigure.swift\n.\n\n\n// Create a new NIO websocket server\n\n\nlet\n \nwss\n \n=\n \nNIOWebSocketServer\n.\ndefault\n()\n\n\n\n// Add WebSocket upgrade support to GET /echo\n\n\nwss\n.\nget\n(\necho\n)\n \n{\n \nws\n,\n \nreq\n \nin\n\n \n// Add a new on text callback\n\n \nws\n.\nonText\n \n{\n \nws\n,\n \ntext\n \nin\n\n \n// Simply echo any received text\n\n \nws\n.\nsend\n(\ntext\n)\n\n \n}\n\n\n}\n\n\n\n// Register our server\n\n\nservices\n.\nregister\n(\nwss\n,\n \nas\n:\n \nWebSocketServer\n.\nself\n)\n\n\n\n\n\n\nThat's it. Next time you boot your server, you will be able to perform a WebSocket upgrade at \nGET /echo\n. You can test this using a simple command line tool called \nwsta\n available for macOS and Linux.\n\n\n$ wsta ws://localhost:8080/echo\nConnected to ws://localhost:8080/echo\nhello, world!\nhello, world!\n\n\n\n\n\nParameters\n\n\nLike Vapor's HTTP router, you can also use routing parameters with your WebSocket server.\n\n\n// Add WebSocket upgrade support to GET /chat/:name\n\n\nwss\n.\nget\n(\nchat\n,\n \nString\n.\nparameter\n)\n \n{\n \nws\n,\n \nreq\n \nin\n\n \nlet\n \nname\n \n=\n \ntry\n \nreq\n.\nparameters\n.\nnext\n(\nString\n.\nself\n)\n\n \nws\n.\nsend\n(\nWelcome, \n\\(\nname\n)\n!\n)\n\n\n \n// ...\n\n\n}\n\n\n\n\n\n\nNow let's test this new route:\n\n\n$ wsta ws://localhost:8080/chat/Vapor\nConnected to ws://localhost:8080/chat/Vapor\nWelcome, Vapor!\n\n\n\n\n\nClient\n\n\nVapor also supports connecting to WebSocket servers as a client. The easiest way to connect to a WebSocket server is through the \nwebSocket(...)\n method on \nClient\n.\n\n\nFor this example, we will assume our application connects to a WebSocket server in \nboot.swift\n\n\n// connect to echo.websocket.org\n\n\nlet\n \ndone\n \n=\n \ntry\n \napp\n.\nclient\n().\nwebSocket\n(\nws://echo.websocket.org\n).\nflatMap\n \n{\n \nws\n \n-\n \nFuture\nVoid\n \nin\n\n \n// setup an on text callback that will print the echo\n\n \nws\n.\nonText\n \n{\n \nws\n,\n \ntext\n \nin\n\n \nprint\n(\nrec: \n\\(\ntext\n)\n)\n\n \n// close the websocket connection after we recv the echo\n\n \nws\n.\nclose\n()\n\n \n}\n\n\n \n// when the websocket first connects, send message\n\n \nws\n.\nsend\n(\nhello, world!\n)\n\n\n \n// return a future that will complete when the websocket closes\n\n \nreturn\n \nws\n.\nonClose\n\n\n}\n\n\n\nprint\n(\ndone\n)\n \n// Future\nVoid\n\n\n\n// wait for the websocket to close\n\n\ntry\n \ndone\n.\nwait\n()",
|
|
"title": "WebSocket"
|
|
},
|
|
{
|
|
"location": "/vapor/websocket/#using-websockets",
|
|
"text": "Vapor includes convenience methods for working with the lower level WebSocket client and server .",
|
|
"title": "Using WebSockets"
|
|
},
|
|
{
|
|
"location": "/vapor/websocket/#server",
|
|
"text": "Vapor's WebSocket server includes the ability to route incoming requests just like its HTTP server. When Vapor's main HTTP Server boots it will attempt to create a WebSocketServer . If one is registered, it will be added as an HTTP upgrade handler to the server. So to create a WebSocket server, all you need to do is register one in configure.swift . // Create a new NIO websocket server let wss = NIOWebSocketServer . default () // Add WebSocket upgrade support to GET /echo wss . get ( echo ) { ws , req in \n // Add a new on text callback \n ws . onText { ws , text in \n // Simply echo any received text \n ws . send ( text ) \n } } // Register our server services . register ( wss , as : WebSocketServer . self ) That's it. Next time you boot your server, you will be able to perform a WebSocket upgrade at GET /echo . You can test this using a simple command line tool called wsta available for macOS and Linux. $ wsta ws://localhost:8080/echo\nConnected to ws://localhost:8080/echo\nhello, world!\nhello, world!",
|
|
"title": "Server"
|
|
},
|
|
{
|
|
"location": "/vapor/websocket/#parameters",
|
|
"text": "Like Vapor's HTTP router, you can also use routing parameters with your WebSocket server. // Add WebSocket upgrade support to GET /chat/:name wss . get ( chat , String . parameter ) { ws , req in \n let name = try req . parameters . next ( String . self ) \n ws . send ( Welcome, \\( name ) ! ) \n\n // ... } Now let's test this new route: $ wsta ws://localhost:8080/chat/Vapor\nConnected to ws://localhost:8080/chat/Vapor\nWelcome, Vapor!",
|
|
"title": "Parameters"
|
|
},
|
|
{
|
|
"location": "/vapor/websocket/#client",
|
|
"text": "Vapor also supports connecting to WebSocket servers as a client. The easiest way to connect to a WebSocket server is through the webSocket(...) method on Client . For this example, we will assume our application connects to a WebSocket server in boot.swift // connect to echo.websocket.org let done = try app . client (). webSocket ( ws://echo.websocket.org ). flatMap { ws - Future Void in \n // setup an on text callback that will print the echo \n ws . onText { ws , text in \n print ( rec: \\( text ) ) \n // close the websocket connection after we recv the echo \n ws . close () \n } \n\n // when the websocket first connects, send message \n ws . send ( hello, world! ) \n\n // return a future that will complete when the websocket closes \n return ws . onClose } print ( done ) // Future Void // wait for the websocket to close try done . wait ()",
|
|
"title": "Client"
|
|
},
|
|
{
|
|
"location": "/websocket/getting-started/",
|
|
"text": "Getting Started with WebSocket\n\n\nWebSocket (\nvapor/websocket\n) is a non-blocking, event-driven WebSocket library built on SwiftNIO. It makes working with SwiftNIO's WebSocket handlers easy and provides integration with \nHTTP\n clients and servers. Creating a WebSocket echo server takes just a few lines of code.\n\n\n\n\nTip\n\n\nIf you use Vapor, most of WebSocket's APIs will be wrapped by more convenient methods. \n\n\n\n\nVapor\n\n\nThis package is included with Vapor and exported by default. You will have access to all \nWebSocket\n APIs when you import \nVapor\n.\n\n\nimport\n \nVapor\n\n\n\n\n\n\nStandalone\n\n\nThe WebSocket package is lightweight, pure Swift, and only depends on SwiftNIO. This means it can be used as a WebSocket framework any Swift project\u2014even one not using Vapor.\n\n\nTo include it in your package, add the following to your \nPackage.swift\n file.\n\n\n// swift-tools-version:4.0\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/websocket.git\n,\n \nfrom\n:\n \n1.0.0\n),\n\n \n],\n\n \ntargets\n:\n \n[\n\n \n.\ntarget\n(\nname\n:\n \nProject\n,\n \ndependencies\n:\n \n[\nWebSocket\n,\n \n...\n \n])\n\n \n]\n\n\n)\n\n\n\n\n\n\nUse \nimport WebSocket\n to access the APIs.\n\n\nThe rest of this guide will give you an overview of what is available in the WebSocket package. As always, feel free to visit the \nAPI docs\n for more in-depth information.",
|
|
"title": "Getting Started"
|
|
},
|
|
{
|
|
"location": "/websocket/getting-started/#getting-started-with-websocket",
|
|
"text": "WebSocket ( vapor/websocket ) is a non-blocking, event-driven WebSocket library built on SwiftNIO. It makes working with SwiftNIO's WebSocket handlers easy and provides integration with HTTP clients and servers. Creating a WebSocket echo server takes just a few lines of code. Tip If you use Vapor, most of WebSocket's APIs will be wrapped by more convenient methods.",
|
|
"title": "Getting Started with WebSocket"
|
|
},
|
|
{
|
|
"location": "/websocket/getting-started/#vapor",
|
|
"text": "This package is included with Vapor and exported by default. You will have access to all WebSocket APIs when you import Vapor . import Vapor",
|
|
"title": "Vapor"
|
|
},
|
|
{
|
|
"location": "/websocket/getting-started/#standalone",
|
|
"text": "The WebSocket package is lightweight, pure Swift, and only depends on SwiftNIO. This means it can be used as a WebSocket framework any Swift project\u2014even one not using Vapor. To include it in your package, add the following to your Package.swift file. // swift-tools-version:4.0 import PackageDescription let package = Package ( \n name : Project , \n dependencies : [ \n ... \n . package ( url : https://github.com/vapor/websocket.git , from : 1.0.0 ), \n ], \n targets : [ \n . target ( name : Project , dependencies : [ WebSocket , ... ]) \n ] ) Use import WebSocket to access the APIs. The rest of this guide will give you an overview of what is available in the WebSocket package. As always, feel free to visit the API docs for more in-depth information.",
|
|
"title": "Standalone"
|
|
},
|
|
{
|
|
"location": "/websocket/overview/",
|
|
"text": "Using WebSockets\n\n\nUnlike HTTP, WebSockets allow you to communicate between client and server in an open, interactive way. You can send messages (called frames) in either text or binary format. Both the client and the server can send as many messages as they want at a time, without having to wait for responses.\n\n\nAlthough WebSocket is its own protocol, it still uses HTTP to get setup. Every WebSocket connection will start with an HTTP request with special headers followed by an HTTP response with status \n101 Switching Protocols\n. After this initial handshake, the connection is a WebSocket connection.\n\n\nWebSocket\n\n\nThe \nWebSocket\n class represents a connected WebSocket client. You can use this to set callbacks for receiving data and to send data.\n\n\nlet\n \nws\n:\n \nWebSocket\n \n=\n \n...\n\n\n// Send an initial message to this WebSocket\n\n\nws\n.\nsend\n(\nHello!\n)\n\n\n\n// Set a new callback for receiving text formatted data\n\n\nws\n.\nonText\n \n{\n \nws\n,\n \nstring\n \nin\n\n \n// Echo the text back, reversed.\n\n \nws\n.\nsend\n(\nstring\n.\nreversed\n())\n\n\n}\n\n\n\n\n\n\n\n\nTip\n\n\nAll callbacks will receive a reference to the \nWebSocket\n. Use these if you need to send data to avoid creating a reference cycle.\n\n\n\n\nThe \nWebSocket\n has an \nonClose\n future that will be completed when the connection closes. You can use \nclose()\n to close the connection yourself.\n\n\nServer\n\n\nWebSocket servers connect to one or more WebSocket clients at a time. As mentioned previously, WebSocket connections must start via an HTTP request and response handshake. Because of this, WebSocket servers are built on top of \nHTTP servers\n using the HTTP upgrade mechanism.\n\n\n// First, create an HTTPProtocolUpgrader\n\n\nlet\n \nws\n \n=\n \nHTTPServer\n.\nwebSocketUpgrader\n(\nshouldUpgrade\n:\n \n{\n \nreq\n \nin\n\n \n// Returning nil in this closure will reject upgrade\n\n \nif\n \nreq\n.\nurl\n.\npath\n \n==\n \n/deny\n \n{\n \nreturn\n \nnil\n \n}\n\n \n// Return any additional headers you like, or just empty\n\n \nreturn\n \n[:]\n\n\n},\n \nonUpgrade\n:\n \n{\n \nws\n,\n \nreq\n \nin\n\n \n// This closure will be called with each new WebSocket client\n\n \nws\n.\nsend\n(\nConnected\n)\n\n \nws\n.\nonText\n \n{\n \nws\n,\n \nstring\n \nin\n\n \nws\n.\nsend\n(\nstring\n.\nreversed\n())\n\n \n}\n\n\n})\n\n\n\n// Next, create your server, adding the WebSocket upgrader\n\n\nlet\n \nserver\n \n=\n \ntry\n \nHTTPServer\n.\nstart\n(\n\n \n...\n\n \nupgraders\n:\n \n[\nws\n],\n\n \n...\n\n\n).\nwait\n()\n\n\n// Run the server.\n\n\ntry\n \nserver\n.\nonClose\n.\nwait\n()\n\n\n\n\n\n\n\n\nSeealso\n\n\nVisit \nHTTP \u2192 Server\n for more information on setting up an HTTP server.\n\n\n\n\nThe WebSocket protocol upgrader consists of two callbacks. \n\n\nThe first callback \nshouldUpgrade\n receives the incoming HTTP request that is requesting upgrade. This callback decides whether or not to complete the upgrade based on the contents of the request. If \nnil\n is returned in this closure, the upgrade will be rejected.\n\n\nThe second callback \nonUpgrade\n is called each time a new WebSocket client connects. This is where you configure your callbacks and send any initial data.\n\n\n\n\nWarning\n\n\nThe upgrade closures may be called on any event loop. Be careful to avoid race conditions if you must access external variables.\n\n\n\n\nClient\n\n\nYou can also use the WebSocket package to connect \nto\n a WebSocket server. Just like the WebSocket server used an HTTP server, the WebSocket client uses HTTP client.\n\n\n// Create a new WebSocket connected to echo.websocket.org\n\n\nlet\n \nws\n \n=\n \ntry\n \nHTTPClient\n.\nwebSocket\n(\nhostname\n:\n \necho.websocket.org\n,\n \non\n:\n \n...).\nwait\n()\n\n\n\n// Set a new callback for receiving text formatted data.\n\n\nws\n.\nonText\n \n{\n \nws\n,\n \ntext\n \nin\n\n \nprint\n(\nServer echo: \n\\(\ntext\n)\n)\n\n\n}\n\n\n\n// Send a message.\n\n\nws\n.\nsend\n(\nHello, world!\n)\n\n\n\n// Wait for the Websocket to closre.\n\n\ntry\n \nws\n.\nonClose\n.\nwait\n()\n\n\n\n\n\n\n\n\nSeealso\n\n\nVisit \nHTTP \u2192 Client\n for more information on setting up an HTTP client.\n\n\n\n\nAPI Docs\n\n\nCheck out the \nAPI docs\n for more in-depth information about all of the methods.",
|
|
"title": "Overview"
|
|
},
|
|
{
|
|
"location": "/websocket/overview/#using-websockets",
|
|
"text": "Unlike HTTP, WebSockets allow you to communicate between client and server in an open, interactive way. You can send messages (called frames) in either text or binary format. Both the client and the server can send as many messages as they want at a time, without having to wait for responses. Although WebSocket is its own protocol, it still uses HTTP to get setup. Every WebSocket connection will start with an HTTP request with special headers followed by an HTTP response with status 101 Switching Protocols . After this initial handshake, the connection is a WebSocket connection.",
|
|
"title": "Using WebSockets"
|
|
},
|
|
{
|
|
"location": "/websocket/overview/#websocket",
|
|
"text": "The WebSocket class represents a connected WebSocket client. You can use this to set callbacks for receiving data and to send data. let ws : WebSocket = ... // Send an initial message to this WebSocket ws . send ( Hello! ) // Set a new callback for receiving text formatted data ws . onText { ws , string in \n // Echo the text back, reversed. \n ws . send ( string . reversed ()) } Tip All callbacks will receive a reference to the WebSocket . Use these if you need to send data to avoid creating a reference cycle. The WebSocket has an onClose future that will be completed when the connection closes. You can use close() to close the connection yourself.",
|
|
"title": "WebSocket"
|
|
},
|
|
{
|
|
"location": "/websocket/overview/#server",
|
|
"text": "WebSocket servers connect to one or more WebSocket clients at a time. As mentioned previously, WebSocket connections must start via an HTTP request and response handshake. Because of this, WebSocket servers are built on top of HTTP servers using the HTTP upgrade mechanism. // First, create an HTTPProtocolUpgrader let ws = HTTPServer . webSocketUpgrader ( shouldUpgrade : { req in \n // Returning nil in this closure will reject upgrade \n if req . url . path == /deny { return nil } \n // Return any additional headers you like, or just empty \n return [:] }, onUpgrade : { ws , req in \n // This closure will be called with each new WebSocket client \n ws . send ( Connected ) \n ws . onText { ws , string in \n ws . send ( string . reversed ()) \n } }) // Next, create your server, adding the WebSocket upgrader let server = try HTTPServer . start ( \n ... \n upgraders : [ ws ], \n ... ). wait () // Run the server. try server . onClose . wait () Seealso Visit HTTP \u2192 Server for more information on setting up an HTTP server. The WebSocket protocol upgrader consists of two callbacks. The first callback shouldUpgrade receives the incoming HTTP request that is requesting upgrade. This callback decides whether or not to complete the upgrade based on the contents of the request. If nil is returned in this closure, the upgrade will be rejected. The second callback onUpgrade is called each time a new WebSocket client connects. This is where you configure your callbacks and send any initial data. Warning The upgrade closures may be called on any event loop. Be careful to avoid race conditions if you must access external variables.",
|
|
"title": "Server"
|
|
},
|
|
{
|
|
"location": "/websocket/overview/#client",
|
|
"text": "You can also use the WebSocket package to connect to a WebSocket server. Just like the WebSocket server used an HTTP server, the WebSocket client uses HTTP client. // Create a new WebSocket connected to echo.websocket.org let ws = try HTTPClient . webSocket ( hostname : echo.websocket.org , on : ...). wait () // Set a new callback for receiving text formatted data. ws . onText { ws , text in \n print ( Server echo: \\( text ) ) } // Send a message. ws . send ( Hello, world! ) // Wait for the Websocket to closre. try ws . onClose . wait () Seealso Visit HTTP \u2192 Client for more information on setting up an HTTP client.",
|
|
"title": "Client"
|
|
},
|
|
{
|
|
"location": "/websocket/overview/#api-docs",
|
|
"text": "Check out the API docs for more in-depth information about all of the methods.",
|
|
"title": "API Docs"
|
|
},
|
|
{
|
|
"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"
|
|
},
|
|
{
|
|
"location": "/version/3_0/#redirecting",
|
|
"text": "",
|
|
"title": "Redirecting..."
|
|
},
|
|
{
|
|
"location": "/version/upgrading/",
|
|
"text": "Upgrading Versions\n\n\nThis document provides information about changes between version and tips for migrating your projects. \n\n\n2.4 to 3.0\n\n\nVapor 3 has been rewritten from the ground up to be async and event-driven. This release contains the most changes of any previous release (and most likely any future release). \n\n\nBecause of this, it is recommended that to migrate your projects you start by creating a new, empty template and migrate by copy / pasting code over to the new project.\n\n\nWe recommend reading the \nGetting Started \n Hello, world!\n section for Vapor 3 to familiarize yourself with the new APIs.\n\n\nAsync\n\n\nThe biggest change in Vapor 3 is that the framework is now completely asynchronous. When you call methods that need to perform slow work like network requests or disk access instead of blocking they will now return a \nFuture\nT\n. \n\n\nFutures are values that may not exist yet, so you cannot interact with them directly. Instead, you must use \nmap\n/\nflatMap\n to access the values.\n\n\n// vapor 2\n\n\nlet\n \nres\n \n=\n \ntry\n \ndrop\n.\nclient\n.\nget\n(\nhttp://vapor.codes\n)\n\n\nprint\n(\nres\n.\nstatus\n)\n \n// HTTPStatus\n\n\nreturn\n \nres\n.\nstatus\n\n\n\n// vapor 3\n\n\nlet\n \nf\n \n=\n \ntry\n \nreq\n.\nclient\n().\nget\n(\nhttp://vapor.codes\n).\nmap\n \n{\n \nres\n \nin\n\n \nprint\n(\nres\n.\nhttp\n.\nstatus\n)\n \n// HTTPStatus\n\n \nreturn\n \nres\n.\nhttp\n.\nstatus\n\n\n}\n\n\nprint\n(\nf\n)\n \n// Future\nHTTPStatus\n\n\n\n\n\n\nSee \nAsync \n Getting Started\n to learn more.\n\n\nApplication \n Services\n\n\nDroplet\n has been renamed to \nApplication\n and is now a service-container. In Vapor 2, the \nDroplet\n had stored properties for things you would need during development (like views, hashers, etc). In Vapor 3, this is all done via services. \n\n\nWhile the \nApplication\n \nis\n a service-container, you should not use it from your route closures. This is to prevent race conditions since Vapor runs on multiple threads (event loops). Instead, use the \nRequest\n that is supplied to your route closure. This has a \ncopy\n of all of the application's services for you to use.\n\n\n// vapor 2\n\n\nreturn\n \ntry\n \ndrop\n.\nview\n.\nmake\n(\nmyView\n)\n\n\n\n// vapor 3\n\n\nreturn\n \ntry\n \nreq\n.\nmake\n(\nViewRenderer\n.\nself\n).\nrender\n(\nmyView\n)\n\n\n// shorthand\n\n\nreturn\n \ntry\n \nreq\n.\nview\n().\nrender\n(\nmyView\n)\n\n\n\n\n\n\nSee \nService \n Getting Started\n to learn more.\n\n\nDatabase Connections\n\n\nIn Vapor 3, database connections are no longer statically accessible. This makes doing things like transactions and connection pooling much more predictable and performant. \n\n\nIn order to create a \nQueryBuilder\n in Fluent 3, you will need access to something \nDatabaseConnectable\n. Most often you can just use the incoming \nRequest\n, but you can also create connections manually if you need. \n\n\n// vapor 2\n\n\nUser\n.\nmakeQuery\n().\nall\n()\n\n\n\n// vapor 3\n\n\nUser\n.\nquery\n(\non\n:\n \nreq\n).\nall\n()\n\n\n\n\n\n\nSee \nDatabaseKit \n Getting Started\n to learn more.\n\n\nWork in progress\n\n\nThis migration guide is a work in progress. Please feel free to add any migration tips here by submitting a PR.\n\n\nJoin the \n#upgrading-to-3\n in Vapor's team chat to ask questions and get help in real time.",
|
|
"title": "Upgrading"
|
|
},
|
|
{
|
|
"location": "/version/upgrading/#upgrading-versions",
|
|
"text": "This document provides information about changes between version and tips for migrating your projects.",
|
|
"title": "Upgrading Versions"
|
|
},
|
|
{
|
|
"location": "/version/upgrading/#24-to-30",
|
|
"text": "Vapor 3 has been rewritten from the ground up to be async and event-driven. This release contains the most changes of any previous release (and most likely any future release). Because of this, it is recommended that to migrate your projects you start by creating a new, empty template and migrate by copy / pasting code over to the new project. We recommend reading the Getting Started Hello, world! section for Vapor 3 to familiarize yourself with the new APIs.",
|
|
"title": "2.4 to 3.0"
|
|
},
|
|
{
|
|
"location": "/version/upgrading/#async",
|
|
"text": "The biggest change in Vapor 3 is that the framework is now completely asynchronous. When you call methods that need to perform slow work like network requests or disk access instead of blocking they will now return a Future T . Futures are values that may not exist yet, so you cannot interact with them directly. Instead, you must use map / flatMap to access the values. // vapor 2 let res = try drop . client . get ( http://vapor.codes ) print ( res . status ) // HTTPStatus return res . status // vapor 3 let f = try req . client (). get ( http://vapor.codes ). map { res in \n print ( res . http . status ) // HTTPStatus \n return res . http . status } print ( f ) // Future HTTPStatus See Async Getting Started to learn more.",
|
|
"title": "Async"
|
|
},
|
|
{
|
|
"location": "/version/upgrading/#application-services",
|
|
"text": "Droplet has been renamed to Application and is now a service-container. In Vapor 2, the Droplet had stored properties for things you would need during development (like views, hashers, etc). In Vapor 3, this is all done via services. While the Application is a service-container, you should not use it from your route closures. This is to prevent race conditions since Vapor runs on multiple threads (event loops). Instead, use the Request that is supplied to your route closure. This has a copy of all of the application's services for you to use. // vapor 2 return try drop . view . make ( myView ) // vapor 3 return try req . make ( ViewRenderer . self ). render ( myView ) // shorthand return try req . view (). render ( myView ) See Service Getting Started to learn more.",
|
|
"title": "Application & Services"
|
|
},
|
|
{
|
|
"location": "/version/upgrading/#database-connections",
|
|
"text": "In Vapor 3, database connections are no longer statically accessible. This makes doing things like transactions and connection pooling much more predictable and performant. In order to create a QueryBuilder in Fluent 3, you will need access to something DatabaseConnectable . Most often you can just use the incoming Request , but you can also create connections manually if you need. // vapor 2 User . makeQuery (). all () // vapor 3 User . query ( on : req ). all () See DatabaseKit Getting Started to learn more.",
|
|
"title": "Database Connections"
|
|
},
|
|
{
|
|
"location": "/version/upgrading/#work-in-progress",
|
|
"text": "This migration guide is a work in progress. Please feel free to add any migration tips here by submitting a PR. Join the #upgrading-to-3 in Vapor's team chat to ask questions and get help in real time.",
|
|
"title": "Work in progress"
|
|
},
|
|
{
|
|
"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"
|
|
}
|
|
]
|
|
} |