mirror of https://github.com/vapor/docs.git
3554 lines
152 KiB
HTML
3554 lines
152 KiB
HTML
|
||
|
||
|
||
|
||
<!DOCTYPE html>
|
||
<html lang="en" class="no-js">
|
||
<head>
|
||
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||
|
||
|
||
<link rel="canonical" href="https://docs.vapor.codes/3.0/extras/style-guide/">
|
||
|
||
|
||
|
||
<meta name="lang:clipboard.copy" content="Copy to clipboard">
|
||
|
||
<meta name="lang:clipboard.copied" content="Copied to clipboard">
|
||
|
||
<meta name="lang:search.language" content="en">
|
||
|
||
<meta name="lang:search.pipeline.stopwords" content="True">
|
||
|
||
<meta name="lang:search.pipeline.trimmer" content="True">
|
||
|
||
<meta name="lang:search.result.none" content="No matching documents">
|
||
|
||
<meta name="lang:search.result.one" content="1 matching document">
|
||
|
||
<meta name="lang:search.result.other" content="# matching documents">
|
||
|
||
<meta name="lang:search.tokenizer" content="[\s\-]+">
|
||
|
||
<link rel="shortcut icon" href="../../assets/images/favicon.png">
|
||
<meta name="generator" content="mkdocs-1.0.4, mkdocs-material-3.0.6">
|
||
|
||
|
||
|
||
<title>Style Guide - Vapor Docs</title>
|
||
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="../../assets/stylesheets/application.451f80e5.css">
|
||
|
||
<link rel="stylesheet" href="../../assets/stylesheets/application-palette.22915126.css">
|
||
|
||
|
||
|
||
|
||
<meta name="theme-color" content="#2196f3">
|
||
|
||
|
||
<link rel="stylesheet" type="text/css" href="../../styles/carbon.css">
|
||
|
||
|
||
|
||
<script src="../../assets/javascripts/modernizr.1aa3b519.js"></script>
|
||
|
||
<script src="../../scripts/carbon.js"></script>
|
||
|
||
|
||
|
||
<link href="https://fonts.gstatic.com" rel="preconnect" crossorigin>
|
||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,400i,700|Roboto+Mono">
|
||
<style>body,input{font-family:"Roboto","Helvetica Neue",Helvetica,Arial,sans-serif}code,kbd,pre{font-family:"Roboto Mono","Courier New",Courier,monospace}</style>
|
||
|
||
|
||
<link rel="stylesheet" href="../../assets/fonts/material-icons.css">
|
||
|
||
|
||
</head>
|
||
|
||
|
||
|
||
<body dir="ltr" data-md-color-primary="blue" data-md-color-accent="purple">
|
||
|
||
<svg class="md-svg">
|
||
<defs>
|
||
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="416" height="448"
|
||
viewBox="0 0 416 448" id="__github">
|
||
<path fill="currentColor" d="M160 304q0 10-3.125 20.5t-10.75 19-18.125
|
||
8.5-18.125-8.5-10.75-19-3.125-20.5 3.125-20.5 10.75-19 18.125-8.5
|
||
18.125 8.5 10.75 19 3.125 20.5zM320 304q0 10-3.125 20.5t-10.75
|
||
19-18.125 8.5-18.125-8.5-10.75-19-3.125-20.5 3.125-20.5 10.75-19
|
||
18.125-8.5 18.125 8.5 10.75 19 3.125 20.5zM360
|
||
304q0-30-17.25-51t-46.75-21q-10.25 0-48.75 5.25-17.75 2.75-39.25
|
||
2.75t-39.25-2.75q-38-5.25-48.75-5.25-29.5 0-46.75 21t-17.25 51q0 22 8
|
||
38.375t20.25 25.75 30.5 15 35 7.375 37.25 1.75h42q20.5 0
|
||
37.25-1.75t35-7.375 30.5-15 20.25-25.75 8-38.375zM416 260q0 51.75-15.25
|
||
82.75-9.5 19.25-26.375 33.25t-35.25 21.5-42.5 11.875-42.875 5.5-41.75
|
||
1.125q-19.5 0-35.5-0.75t-36.875-3.125-38.125-7.5-34.25-12.875-30.25-20.25-21.5-28.75q-15.5-30.75-15.5-82.75
|
||
0-59.25 34-99-6.75-20.5-6.75-42.5 0-29 12.75-54.5 27 0 47.5 9.875t47.25
|
||
30.875q36.75-8.75 77.25-8.75 37 0 70 8 26.25-20.5
|
||
46.75-30.25t47.25-9.75q12.75 25.5 12.75 54.5 0 21.75-6.75 42 34 40 34
|
||
99.5z" />
|
||
</svg>
|
||
|
||
</defs>
|
||
</svg>
|
||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
||
<label class="md-overlay" data-md-component="overlay" for="__drawer"></label>
|
||
|
||
<a href="#vapor-style-guide" tabindex="1" class="md-skip">
|
||
Skip to content
|
||
</a>
|
||
|
||
|
||
<header class="md-header" data-md-component="header">
|
||
<nav class="md-header-nav md-grid">
|
||
<div class="md-flex">
|
||
<div class="md-flex__cell md-flex__cell--shrink">
|
||
<a href="https://docs.vapor.codes/3.0/" title="Vapor Docs" class="md-header-nav__button md-logo">
|
||
|
||
<img src="../../images/droplet-white.svg" width="24" height="24">
|
||
|
||
</a>
|
||
</div>
|
||
<div class="md-flex__cell md-flex__cell--shrink">
|
||
<label class="md-icon md-icon--menu md-header-nav__button" for="__drawer"></label>
|
||
</div>
|
||
<div class="md-flex__cell md-flex__cell--stretch">
|
||
<div class="md-flex__ellipsis md-header-nav__title" data-md-component="title">
|
||
|
||
|
||
<span class="md-header-nav__topic">
|
||
Vapor Docs
|
||
</span>
|
||
<span class="md-header-nav__topic">
|
||
Style Guide
|
||
</span>
|
||
|
||
|
||
</div>
|
||
</div>
|
||
<div class="md-flex__cell md-flex__cell--shrink">
|
||
|
||
|
||
<label class="md-icon md-icon--search md-header-nav__button" for="__search"></label>
|
||
|
||
<div class="md-search" data-md-component="search" role="dialog">
|
||
<label class="md-search__overlay" for="__search"></label>
|
||
<div class="md-search__inner" role="search">
|
||
<form class="md-search__form" name="search">
|
||
<input type="text" class="md-search__input" name="query" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="query" data-md-state="active">
|
||
<label class="md-icon md-search__icon" for="__search"></label>
|
||
<button type="reset" class="md-icon md-search__icon" data-md-component="reset" tabindex="-1">
|
||

|
||
</button>
|
||
</form>
|
||
<div class="md-search__output">
|
||
<div class="md-search__scrollwrap" data-md-scrollfix>
|
||
<div class="md-search-result" data-md-component="result">
|
||
<div class="md-search-result__meta">
|
||
Type to start searching
|
||
</div>
|
||
<ol class="md-search-result__list"></ol>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
</div>
|
||
|
||
<div class="md-flex__cell md-flex__cell--shrink">
|
||
<div class="md-header-nav__source">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<a href="http://github.com/vapor/vapor/" title="Go to repository" class="md-source" data-md-source="github">
|
||
|
||
<div class="md-source__icon">
|
||
<svg viewBox="0 0 24 24" width="24" height="24">
|
||
<use xlink:href="#__github" width="24" height="24"></use>
|
||
</svg>
|
||
</div>
|
||
|
||
<div class="md-source__repository">
|
||
GitHub
|
||
</div>
|
||
</a>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</nav>
|
||
</header>
|
||
|
||
<div class="md-container">
|
||
|
||
|
||
|
||
|
||
<main class="md-main">
|
||
<div class="md-main__inner md-grid" data-md-component="container">
|
||
|
||
|
||
<div class="md-sidebar md-sidebar--primary" data-md-component="navigation">
|
||
<div class="md-sidebar__scrollwrap">
|
||
<div class="md-sidebar__inner">
|
||
<nav class="md-nav md-nav--primary" data-md-level="0">
|
||
<label class="md-nav__title md-nav__title--site" for="__drawer">
|
||
<a href="https://docs.vapor.codes/3.0/" title="Vapor Docs" class="md-nav__button md-logo">
|
||
|
||
<img src="../../images/droplet-white.svg" width="48" height="48">
|
||
|
||
</a>
|
||
Vapor Docs
|
||
</label>
|
||
|
||
<div class="md-nav__source">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<a href="http://github.com/vapor/vapor/" title="Go to repository" class="md-source" data-md-source="github">
|
||
|
||
<div class="md-source__icon">
|
||
<svg viewBox="0 0 24 24" width="24" height="24">
|
||
<use xlink:href="#__github" width="24" height="24"></use>
|
||
</svg>
|
||
</div>
|
||
|
||
<div class="md-source__repository">
|
||
GitHub
|
||
</div>
|
||
</a>
|
||
|
||
</div>
|
||
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../.." title="Overview" class="md-nav__link">
|
||
Overview
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-2" type="checkbox" id="nav-2">
|
||
|
||
<label class="md-nav__link" for="nav-2">
|
||
Install
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-2">
|
||
Install
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../install/macos/" title="macOS" class="md-nav__link">
|
||
macOS
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../install/ubuntu/" title="Ubuntu" class="md-nav__link">
|
||
Ubuntu
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-3" type="checkbox" id="nav-3">
|
||
|
||
<label class="md-nav__link" for="nav-3">
|
||
Getting Started
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-3">
|
||
Getting Started
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../getting-started/hello-world/" title="Hello, world" class="md-nav__link">
|
||
Hello, world
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../getting-started/toolbox/" title="Toolbox" class="md-nav__link">
|
||
Toolbox
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../getting-started/spm/" title="SPM" class="md-nav__link">
|
||
SPM
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../getting-started/xcode/" title="Xcode" class="md-nav__link">
|
||
Xcode
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../getting-started/structure/" title="Folder Structure" class="md-nav__link">
|
||
Folder Structure
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../getting-started/application/" title="Application" class="md-nav__link">
|
||
Application
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../getting-started/controllers/" title="Controllers" class="md-nav__link">
|
||
Controllers
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../getting-started/routing/" title="Routing" class="md-nav__link">
|
||
Routing
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../getting-started/content/" title="Content" class="md-nav__link">
|
||
Content
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../getting-started/async/" title="Async" class="md-nav__link">
|
||
Async
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../getting-started/services/" title="Services" class="md-nav__link">
|
||
Services
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../getting-started/cloud/" title="Deployment" class="md-nav__link">
|
||
Deployment
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-4" type="checkbox" id="nav-4">
|
||
|
||
<label class="md-nav__link" for="nav-4">
|
||
Async
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-4">
|
||
Async
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../async/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../async/overview/" title="Overview" class="md-nav__link">
|
||
Overview
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-5" type="checkbox" id="nav-5">
|
||
|
||
<label class="md-nav__link" for="nav-5">
|
||
Auth
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-5">
|
||
Auth
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../auth/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../auth/api/" title="Stateless (API)" class="md-nav__link">
|
||
Stateless (API)
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../auth/web/" title="Sessions (Web)" class="md-nav__link">
|
||
Sessions (Web)
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-6" type="checkbox" id="nav-6">
|
||
|
||
<label class="md-nav__link" for="nav-6">
|
||
Console
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-6">
|
||
Console
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../console/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../console/overview/" title="Overview" class="md-nav__link">
|
||
Overview
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-7" type="checkbox" id="nav-7">
|
||
|
||
<label class="md-nav__link" for="nav-7">
|
||
Command
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-7">
|
||
Command
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../command/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../command/overview/" title="Overview" class="md-nav__link">
|
||
Overview
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-8" type="checkbox" id="nav-8">
|
||
|
||
<label class="md-nav__link" for="nav-8">
|
||
Crypto
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-8">
|
||
Crypto
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../crypto/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../crypto/digests/" title="Digests" class="md-nav__link">
|
||
Digests
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../crypto/ciphers/" title="Ciphers" class="md-nav__link">
|
||
Ciphers
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../crypto/asymmetric/" title="Asymmetric" class="md-nav__link">
|
||
Asymmetric
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../crypto/random/" title="Random" class="md-nav__link">
|
||
Random
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../crypto/otp/" title="TOTP & HOTP" class="md-nav__link">
|
||
TOTP & HOTP
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-9" type="checkbox" id="nav-9">
|
||
|
||
<label class="md-nav__link" for="nav-9">
|
||
Database Kit
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-9">
|
||
Database Kit
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../database-kit/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../database-kit/overview/" title="Overview" class="md-nav__link">
|
||
Overview
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-10" type="checkbox" id="nav-10">
|
||
|
||
<label class="md-nav__link" for="nav-10">
|
||
Fluent
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-10">
|
||
Fluent
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../fluent/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../fluent/models/" title="Models" class="md-nav__link">
|
||
Models
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../fluent/querying/" title="Querying" class="md-nav__link">
|
||
Querying
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../fluent/migrations/" title="Migrations" class="md-nav__link">
|
||
Migrations
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../fluent/relations/" title="Relations" class="md-nav__link">
|
||
Relations
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../fluent/transaction/" title="Transaction" class="md-nav__link">
|
||
Transaction
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-11" type="checkbox" id="nav-11">
|
||
|
||
<label class="md-nav__link" for="nav-11">
|
||
HTTP
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-11">
|
||
HTTP
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../http/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../http/client/" title="Client" class="md-nav__link">
|
||
Client
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../http/server/" title="Server" class="md-nav__link">
|
||
Server
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../http/message/" title="Message" class="md-nav__link">
|
||
Message
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-12" type="checkbox" id="nav-12">
|
||
|
||
<label class="md-nav__link" for="nav-12">
|
||
JWT
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-12">
|
||
JWT
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../jwt/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../jwt/overview/" title="Overview" class="md-nav__link">
|
||
Overview
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-13" type="checkbox" id="nav-13">
|
||
|
||
<label class="md-nav__link" for="nav-13">
|
||
Leaf
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-13">
|
||
Leaf
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../leaf/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../leaf/overview/" title="Overview" class="md-nav__link">
|
||
Overview
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../leaf/custom-tags/" title="Custom tags" class="md-nav__link">
|
||
Custom tags
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-14" type="checkbox" id="nav-14">
|
||
|
||
<label class="md-nav__link" for="nav-14">
|
||
Logging
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-14">
|
||
Logging
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../logging/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../logging/overview/" title="Overview" class="md-nav__link">
|
||
Overview
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-15" type="checkbox" id="nav-15">
|
||
|
||
<label class="md-nav__link" for="nav-15">
|
||
Multipart
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-15">
|
||
Multipart
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../multipart/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../multipart/overview/" title="Overview" class="md-nav__link">
|
||
Overview
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-16" type="checkbox" id="nav-16">
|
||
|
||
<label class="md-nav__link" for="nav-16">
|
||
MySQL
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-16">
|
||
MySQL
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../mysql/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-17" type="checkbox" id="nav-17">
|
||
|
||
<label class="md-nav__link" for="nav-17">
|
||
PostgreSQL
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-17">
|
||
PostgreSQL
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../postgresql/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-18" type="checkbox" id="nav-18">
|
||
|
||
<label class="md-nav__link" for="nav-18">
|
||
Redis
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-18">
|
||
Redis
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../redis/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../redis/overview/" title="Overview" class="md-nav__link">
|
||
Overview
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-19" type="checkbox" id="nav-19">
|
||
|
||
<label class="md-nav__link" for="nav-19">
|
||
Routing
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-19">
|
||
Routing
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../routing/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../routing/overview/" title="Overview" class="md-nav__link">
|
||
Overview
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-20" type="checkbox" id="nav-20">
|
||
|
||
<label class="md-nav__link" for="nav-20">
|
||
Service
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-20">
|
||
Service
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../service/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../service/services/" title="Services" class="md-nav__link">
|
||
Services
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../service/provider/" title="Provider" class="md-nav__link">
|
||
Provider
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-21" type="checkbox" id="nav-21">
|
||
|
||
<label class="md-nav__link" for="nav-21">
|
||
SQL
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-21">
|
||
SQL
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../sql/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../sql/overview/" title="Overview" class="md-nav__link">
|
||
Overview
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-22" type="checkbox" id="nav-22">
|
||
|
||
<label class="md-nav__link" for="nav-22">
|
||
SQLite
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-22">
|
||
SQLite
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../sqlite/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-23" type="checkbox" id="nav-23">
|
||
|
||
<label class="md-nav__link" for="nav-23">
|
||
Template Kit
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-23">
|
||
Template Kit
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../template-kit/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-24" type="checkbox" id="nav-24">
|
||
|
||
<label class="md-nav__link" for="nav-24">
|
||
Testing
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-24">
|
||
Testing
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../testing/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-25" type="checkbox" id="nav-25">
|
||
|
||
<label class="md-nav__link" for="nav-25">
|
||
URL-Encoded Form
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-25">
|
||
URL-Encoded Form
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../url-encoded-form/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../url-encoded-form/overview/" title="Overview" class="md-nav__link">
|
||
Overview
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-26" type="checkbox" id="nav-26">
|
||
|
||
<label class="md-nav__link" for="nav-26">
|
||
Validation
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-26">
|
||
Validation
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../validation/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../validation/overview/" title="Overview" class="md-nav__link">
|
||
Overview
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-27" type="checkbox" id="nav-27">
|
||
|
||
<label class="md-nav__link" for="nav-27">
|
||
Vapor
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-27">
|
||
Vapor
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../vapor/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../vapor/client/" title="Client" class="md-nav__link">
|
||
Client
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../vapor/content/" title="Content" class="md-nav__link">
|
||
Content
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../vapor/sessions/" title="Sessions" class="md-nav__link">
|
||
Sessions
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../vapor/websocket/" title="WebSocket" class="md-nav__link">
|
||
WebSocket
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../vapor/middleware/" title="Middleware" class="md-nav__link">
|
||
Middleware
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-28" type="checkbox" id="nav-28">
|
||
|
||
<label class="md-nav__link" for="nav-28">
|
||
WebSocket
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-28">
|
||
WebSocket
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../websocket/getting-started/" title="Getting Started" class="md-nav__link">
|
||
Getting Started
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../websocket/overview/" title="Overview" class="md-nav__link">
|
||
Overview
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--active md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-29" type="checkbox" id="nav-29" checked>
|
||
|
||
<label class="md-nav__link" for="nav-29">
|
||
Extras
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-29">
|
||
Extras
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--active">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="toc" type="checkbox" id="__toc">
|
||
|
||
|
||
|
||
|
||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||
Style Guide
|
||
</label>
|
||
|
||
<a href="./" title="Style Guide" class="md-nav__link md-nav__link--active">
|
||
Style Guide
|
||
</a>
|
||
|
||
|
||
<nav class="md-nav md-nav--secondary">
|
||
|
||
|
||
|
||
|
||
|
||
<label class="md-nav__title" for="__toc">Table of contents</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#motivation" title="Motivation" class="md-nav__link">
|
||
Motivation
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#contributing" title="Contributing" class="md-nav__link">
|
||
Contributing
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#application-structure" title="Application Structure" class="md-nav__link">
|
||
Application Structure
|
||
</a>
|
||
|
||
<nav class="md-nav">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#controllers-folder" title="Controllers Folder" class="md-nav__link">
|
||
Controllers Folder
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#middleware-folder" title="Middleware Folder" class="md-nav__link">
|
||
Middleware Folder
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#models-folder" title="Models Folder" class="md-nav__link">
|
||
Models Folder
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#setup-folder" title="Setup Folder" class="md-nav__link">
|
||
Setup Folder
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#utilities-folder" title="Utilities Folder" class="md-nav__link">
|
||
Utilities Folder
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#services-folder" title="Services Folder" class="md-nav__link">
|
||
Services Folder
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#configuration" title="Configuration" class="md-nav__link">
|
||
Configuration
|
||
</a>
|
||
|
||
<nav class="md-nav">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#files" title="Files" class="md-nav__link">
|
||
Files
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#configureswift" title="configure.swift" class="md-nav__link">
|
||
configure.swift
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#routesswift" title="routes.swift" class="md-nav__link">
|
||
routes.swift
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#commandsswift" title="commands.swift" class="md-nav__link">
|
||
commands.swift
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#contentswift" title="content.swift" class="md-nav__link">
|
||
content.swift
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#databasesswift" title="databases.swift" class="md-nav__link">
|
||
databases.swift
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#middlewaresswift" title="middlewares.swift" class="md-nav__link">
|
||
middlewares.swift
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#migrateswift" title="migrate.swift" class="md-nav__link">
|
||
migrate.swift
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#repositoriesswift" title="repositories.swift" class="md-nav__link">
|
||
repositories.swift
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#credentials" title="Credentials" class="md-nav__link">
|
||
Credentials
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#file-naming" title="File Naming" class="md-nav__link">
|
||
File Naming
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#architecture" title="Architecture" class="md-nav__link">
|
||
Architecture
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#entities" title="Entities" class="md-nav__link">
|
||
Entities
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#routes-and-controllers" title="Routes and Controllers" class="md-nav__link">
|
||
Routes and Controllers
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#async" title="Async" class="md-nav__link">
|
||
Async
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#testing" title="Testing" class="md-nav__link">
|
||
Testing
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#fluent" title="Fluent" class="md-nav__link">
|
||
Fluent
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#leaf" title="Leaf" class="md-nav__link">
|
||
Leaf
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#errors" title="Errors" class="md-nav__link">
|
||
Errors
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#3rd-party-providers" title="3rd Party Providers" class="md-nav__link">
|
||
3rd Party Providers
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#overall-advice" title="Overall Advice" class="md-nav__link">
|
||
Overall Advice
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#maintainers" title="Maintainers" class="md-nav__link">
|
||
Maintainers
|
||
</a>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#__comments" title="Comments" class="md-nav__link md-nav__link--active">
|
||
Comments
|
||
</a>
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../yeoman/" title="Yeoman" class="md-nav__link">
|
||
Yeoman
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
<input class="md-toggle md-nav__toggle" data-md-toggle="nav-30" type="checkbox" id="nav-30">
|
||
|
||
<label class="md-nav__link" for="nav-30">
|
||
Version (3.0)
|
||
</label>
|
||
<nav class="md-nav" data-md-component="collapsible" data-md-level="1">
|
||
<label class="md-nav__title" for="nav-30">
|
||
Version (3.0)
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../version/1_5/" title="1.5" class="md-nav__link">
|
||
1.5
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../version/2_0/" title="2.0" class="md-nav__link">
|
||
2.0
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../version/3_0/" title="3.0" class="md-nav__link">
|
||
3.0
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../version/upgrading/" title="Upgrading" class="md-nav__link">
|
||
Upgrading
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../../version/support/" title="Support" class="md-nav__link">
|
||
Support
|
||
</a>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</li>
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div class="md-sidebar md-sidebar--secondary" data-md-component="toc">
|
||
<div class="md-sidebar__scrollwrap">
|
||
<div class="md-sidebar__inner">
|
||
|
||
<nav class="md-nav md-nav--secondary">
|
||
|
||
|
||
|
||
|
||
|
||
<label class="md-nav__title" for="__toc">Table of contents</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#motivation" title="Motivation" class="md-nav__link">
|
||
Motivation
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#contributing" title="Contributing" class="md-nav__link">
|
||
Contributing
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#application-structure" title="Application Structure" class="md-nav__link">
|
||
Application Structure
|
||
</a>
|
||
|
||
<nav class="md-nav">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#controllers-folder" title="Controllers Folder" class="md-nav__link">
|
||
Controllers Folder
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#middleware-folder" title="Middleware Folder" class="md-nav__link">
|
||
Middleware Folder
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#models-folder" title="Models Folder" class="md-nav__link">
|
||
Models Folder
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#setup-folder" title="Setup Folder" class="md-nav__link">
|
||
Setup Folder
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#utilities-folder" title="Utilities Folder" class="md-nav__link">
|
||
Utilities Folder
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#services-folder" title="Services Folder" class="md-nav__link">
|
||
Services Folder
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#configuration" title="Configuration" class="md-nav__link">
|
||
Configuration
|
||
</a>
|
||
|
||
<nav class="md-nav">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#files" title="Files" class="md-nav__link">
|
||
Files
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#configureswift" title="configure.swift" class="md-nav__link">
|
||
configure.swift
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#routesswift" title="routes.swift" class="md-nav__link">
|
||
routes.swift
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#commandsswift" title="commands.swift" class="md-nav__link">
|
||
commands.swift
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#contentswift" title="content.swift" class="md-nav__link">
|
||
content.swift
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#databasesswift" title="databases.swift" class="md-nav__link">
|
||
databases.swift
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#middlewaresswift" title="middlewares.swift" class="md-nav__link">
|
||
middlewares.swift
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#migrateswift" title="migrate.swift" class="md-nav__link">
|
||
migrate.swift
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#repositoriesswift" title="repositories.swift" class="md-nav__link">
|
||
repositories.swift
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#credentials" title="Credentials" class="md-nav__link">
|
||
Credentials
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#file-naming" title="File Naming" class="md-nav__link">
|
||
File Naming
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#architecture" title="Architecture" class="md-nav__link">
|
||
Architecture
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#entities" title="Entities" class="md-nav__link">
|
||
Entities
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#routes-and-controllers" title="Routes and Controllers" class="md-nav__link">
|
||
Routes and Controllers
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#async" title="Async" class="md-nav__link">
|
||
Async
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#testing" title="Testing" class="md-nav__link">
|
||
Testing
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#fluent" title="Fluent" class="md-nav__link">
|
||
Fluent
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#leaf" title="Leaf" class="md-nav__link">
|
||
Leaf
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#errors" title="Errors" class="md-nav__link">
|
||
Errors
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#3rd-party-providers" title="3rd Party Providers" class="md-nav__link">
|
||
3rd Party Providers
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#overall-advice" title="Overall Advice" class="md-nav__link">
|
||
Overall Advice
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#maintainers" title="Maintainers" class="md-nav__link">
|
||
Maintainers
|
||
</a>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#__comments" title="Comments" class="md-nav__link md-nav__link--active">
|
||
Comments
|
||
</a>
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div class="md-content">
|
||
<article class="md-content__inner md-typeset">
|
||
|
||
|
||
<a href="https://github.com/vapor/documentation/edit/master/3.0/docs/extras/style-guide.md" title="Edit this page" class="md-icon md-content__icon"></a>
|
||
|
||
|
||
<h1 id="vapor-style-guide">Vapor Style Guide<a class="headerlink" href="#vapor-style-guide" title="Permanent link">¶</a></h1>
|
||
<h2 id="motivation">Motivation<a class="headerlink" href="#motivation" title="Permanent link">¶</a></h2>
|
||
<p>The Vapor style guide is a perspective on how to write Vapor application code that is clean, readable, and maintainable. It can serve as a jumping off point within your organization (or yourself) for how to write code in a style that aligns with the Vapor ecosystem. We think this guide can help solidify common ideas that occur across most applications and will be a reference for maintainers when starting a new project. This style guide is opinionated, so you should adapt your code in places where you don’t agree.</p>
|
||
<h2 id="contributing">Contributing<a class="headerlink" href="#contributing" title="Permanent link">¶</a></h2>
|
||
<p>To contribute to this guide, please submit a pull request that includes your proposed changes as well as logic to support your addition or modification. Pull requests will be reviewed by the maintainers and the rationale behind the maintainers’ decision to accept or deny the changes will be posted in the pull request.</p>
|
||
<h2 id="application-structure">Application Structure<a class="headerlink" href="#application-structure" title="Permanent link">¶</a></h2>
|
||
<p>The structure of your Vapor application is important from a readability standpoint, but also in terms of functionality. Application structure refers to a few different aspects of the Vapor ecosystem, but in particular, it is the way in which you structure your file, folders, and assets.</p>
|
||
<p>The preferred way to structure your application is by separating the application into a few main parts:</p>
|
||
<ul>
|
||
<li>Controllers</li>
|
||
<li>Middleware</li>
|
||
<li>Models</li>
|
||
<li>Setup</li>
|
||
<li>Utilities</li>
|
||
<li>Services</li>
|
||
</ul>
|
||
<p>The structure ensures that new members working on your project can easily find the file or asset they are looking for.</p>
|
||
<h4 id="controllers-folder">Controllers Folder<a class="headerlink" href="#controllers-folder" title="Permanent link">¶</a></h4>
|
||
<p>The controllers folder houses all of the controllers for your application which correspond to your routes. If you are building an application that serves both API responses and frontend responses, this folder should be further segmented into an <code>API Controllers</code> folder and a <code>View Controllers</code> folder.</p>
|
||
<h4 id="middleware-folder">Middleware Folder<a class="headerlink" href="#middleware-folder" title="Permanent link">¶</a></h4>
|
||
<p>The middleware folder contains any custom middleware that you’ve written for your application. Each piece of middleware should be descriptively named and should only be responsible for one piece of functionality.</p>
|
||
<h4 id="models-folder">Models Folder<a class="headerlink" href="#models-folder" title="Permanent link">¶</a></h4>
|
||
<p>“Models” in this document means an object that can be used to store or return data throughout the application. Models are not specific to Fluent - Entities, however, include database information that make it possible to persist and query them.</p>
|
||
<p>The Models folder should be broken down into four parts: Entities, Requests, Responses, and View Contexts (if applicable to your application). The <code>Requests</code> and <code>Responses</code> folder hold object files that are used to decode requests or encode responses. For more information on this, see the “File Naming” section.</p>
|
||
<p>If your application handles view rendering via Leaf, you should also have a folder that holds all of your view contexts. These contexts are the same type of objects as the Request and Response objects, but are specifically for passing data to the view layer.</p>
|
||
<p>The Entities folder is further broken up into a folder for each database model that exists within your application. For example, if you have a <code>User</code> model that represents a <code>users</code> table, you would have a <code>Users</code> folder that contains <code>User.swift</code> (the Fluent model representation) and then any other applicable files for this entity. Other common files found at this level include files to extend functionality of the object, repository protocols/implementations, and data transformation extensions.</p>
|
||
<h4 id="setup-folder">Setup Folder<a class="headerlink" href="#setup-folder" title="Permanent link">¶</a></h4>
|
||
<p>The setup folder has all of the necessary pieces that are called on application setup. This includes <code>app.swift</code>, <code>boot.swift</code>, <code>configure.swift</code>, <code>migrate.swift</code>, and <code>routes.swift</code>. For information on each of these files, see the “Configuration” section.</p>
|
||
<h4 id="utilities-folder">Utilities Folder<a class="headerlink" href="#utilities-folder" title="Permanent link">¶</a></h4>
|
||
<p>The utilities folder serves as a general purpose location for any objects or helpers that don’t fit the other folders. For example, in your quest to eliminate stringly-typed code (see the “General Advice” section) you might place a <code>Constants.swift</code> file in this location.</p>
|
||
<h4 id="services-folder">Services Folder<a class="headerlink" href="#services-folder" title="Permanent link">¶</a></h4>
|
||
<p>The services folder is used to hold any custom services that are created and registered. </p>
|
||
<p>The final application structure (inside the Sources folder) looks like this:</p>
|
||
<div class="codehilite"><pre><span></span>├── Controllers
|
||
│ ├── API\ Controllers
|
||
│ └── View\ Controllers
|
||
├── Middleware
|
||
├── Models
|
||
│ ├── Entities
|
||
│ │ └── User
|
||
│ ├── Requests
|
||
│ └── Responses
|
||
│ └── View\ Contexts
|
||
├── Setup
|
||
│ ├── app.swift
|
||
│ ├── boot.swift
|
||
│ ├── commands.swift
|
||
│ ├── configure.swift
|
||
│ ├── content.swift
|
||
│ ├── databases.swift
|
||
│ ├── middlewares.swift
|
||
│ ├── migrate.swift
|
||
│ ├── repositories.swift
|
||
│ └── routes.swift
|
||
├── Utilities
|
||
├── Services
|
||
</pre></div>
|
||
|
||
|
||
<h2 id="configuration">Configuration<a class="headerlink" href="#configuration" title="Permanent link">¶</a></h2>
|
||
<p>Configuring your application correctly is one of the most important parts of a successful Vapor application. The main function of the configuring a Vapor application is correctly registering all of your services and 3rd party providers.</p>
|
||
<p><strong>Note</strong>: For more information on registering credentials and secrets, see the “Credentials” section.</p>
|
||
<h4 id="files">Files<a class="headerlink" href="#files" title="Permanent link">¶</a></h4>
|
||
<p>Depending on your application you should have some or all of the following files:</p>
|
||
<ul>
|
||
<li>app.swift (use the default template version)</li>
|
||
<li>boot.swift (use the default template version)</li>
|
||
<li>commands.swift (Optional)</li>
|
||
<li>configure.swift</li>
|
||
<li>content.swift</li>
|
||
<li>databases.swift (Optional)</li>
|
||
<li>middlewares.swift</li>
|
||
<li>migrate.swift (Optional)</li>
|
||
<li>repositories.swift (Optional)</li>
|
||
<li>routes.swift</li>
|
||
</ul>
|
||
<h4 id="configureswift">configure.swift<a class="headerlink" href="#configureswift" title="Permanent link">¶</a></h4>
|
||
<p>Use this file to register your services, providers, and any other code that needs to run as part of the Vapor application setup process.</p>
|
||
<p>We recommend registering all services (with some exceptions, like <code>BlockingIOThreadPool</code>, that have internal synchronization code) using the closure method. The closure gets called each time a container requests that service. There's one container per thread, meaning that you get one service per thread. As a result, you don't need to think about synchronizing access and state in the object, which is otherwise difficult. The tradeoff to this method is memory usage, which is typically negligible for a small class, but you gain performance.</p>
|
||
<h4 id="routesswift">routes.swift<a class="headerlink" href="#routesswift" title="Permanent link">¶</a></h4>
|
||
<p>The routes.swift file is used to declare route registration for your application. Typically, the routes.swift file looks like this:</p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">import</span> <span class="nc">Vapor</span>
|
||
|
||
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">routes</span><span class="p">(</span><span class="kc">_</span> <span class="n">router</span><span class="p">:</span> <span class="n">Router</span><span class="p">,</span> <span class="kc">_</span> <span class="n">container</span><span class="p">:</span> <span class="n">Container</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">{</span>
|
||
<span class="k">try</span> <span class="n">router</span><span class="p">.</span><span class="n">register</span><span class="p">(</span><span class="n">collection</span><span class="p">:</span> <span class="n">MyControllerHere</span><span class="p">(</span><span class="n">db</span><span class="p">:</span> <span class="n">container</span><span class="p">.</span><span class="n">connectionPool</span><span class="p">(</span><span class="n">to</span><span class="p">:</span> <span class="p">.</span><span class="n">mysql</span><span class="p">)))</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>You should call this function from <code>configure.swift</code> like this:</p>
|
||
<div class="codehilite"><pre><span></span> <span class="n">services</span><span class="p">.</span><span class="n">register</span><span class="p">(</span><span class="n">Router</span><span class="p">.</span><span class="kc">self</span><span class="p">)</span> <span class="p">{</span> <span class="n">container</span> <span class="p">-></span> <span class="n">EngineRouter</span> <span class="k">in</span>
|
||
<span class="kd">let</span> <span class="nv">router</span> <span class="p">=</span> <span class="n">EngineRouter</span><span class="p">.</span><span class="k">default</span><span class="p">()</span>
|
||
<span class="k">try</span> <span class="n">routes</span><span class="p">(</span><span class="n">router</span><span class="p">,</span> <span class="n">container</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">router</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>For more information on routes, see the “Routes and Controllers” section.</p>
|
||
<h4 id="commandsswift">commands.swift<a class="headerlink" href="#commandsswift" title="Permanent link">¶</a></h4>
|
||
<p>Use this file to add your custom commands to your application. For example:</p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">import</span> <span class="nc">Vapor</span>
|
||
|
||
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">commands</span><span class="p">(</span><span class="n">config</span><span class="p">:</span> <span class="kr">inout</span> <span class="n">CommandConfig</span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="n">config</span><span class="p">.</span><span class="n">useFluentCommands</span><span class="p">()</span>
|
||
|
||
<span class="n">config</span><span class="p">.</span><span class="n">use</span><span class="p">(</span><span class="n">MyCustomCommand</span><span class="p">(),</span> <span class="k">as</span><span class="p">:</span> <span class="s">"my-custom-command"</span><span class="p">)</span>
|
||
<span class="p">...</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>You should call this function from <code>configure.swift</code> like this:</p>
|
||
<div class="codehilite"><pre><span></span> <span class="c1">/// Command Config</span>
|
||
<span class="kd">var</span> <span class="nv">commandsConfig</span> <span class="p">=</span> <span class="n">CommandConfig</span><span class="p">.</span><span class="k">default</span><span class="p">()</span>
|
||
<span class="n">commands</span><span class="p">(</span><span class="n">config</span><span class="p">:</span> <span class="p">&</span><span class="n">commandsConfig</span><span class="p">)</span>
|
||
<span class="n">services</span><span class="p">.</span><span class="n">register</span><span class="p">(</span><span class="n">commandsConfig</span><span class="p">)</span>
|
||
</pre></div>
|
||
|
||
|
||
<blockquote>
|
||
<p>If your app doesn't use custom <code>Command</code>s you can omit this file.</p>
|
||
</blockquote>
|
||
<h4 id="contentswift">content.swift<a class="headerlink" href="#contentswift" title="Permanent link">¶</a></h4>
|
||
<p>In this file you can customize the content encoding/decoding configuration for your data models. For example:</p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">import</span> <span class="nc">Vapor</span>
|
||
|
||
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">content</span><span class="p">(</span><span class="n">config</span><span class="p">:</span> <span class="kr">inout</span> <span class="n">ContentConfig</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">{</span>
|
||
<span class="kd">let</span> <span class="nv">encoder</span> <span class="p">=</span> <span class="n">JSONEncoder</span><span class="p">()</span>
|
||
<span class="kd">let</span> <span class="nv">decoder</span> <span class="p">=</span> <span class="n">JSONDecoder</span><span class="p">()</span>
|
||
|
||
<span class="n">encoder</span><span class="p">.</span><span class="n">dateEncodingStrategy</span> <span class="p">=</span> <span class="p">.</span><span class="n">millisecondsSince1970</span>
|
||
<span class="n">decoder</span><span class="p">.</span><span class="n">dateDecodingStrategy</span> <span class="p">=</span> <span class="p">.</span><span class="n">millisecondsSince1970</span>
|
||
|
||
<span class="n">config</span><span class="p">.</span><span class="n">use</span><span class="p">(</span><span class="n">encoder</span><span class="p">:</span> <span class="n">encoder</span><span class="p">,</span> <span class="k">for</span><span class="p">:</span> <span class="p">.</span><span class="n">json</span><span class="p">)</span>
|
||
<span class="n">config</span><span class="p">.</span><span class="n">use</span><span class="p">(</span><span class="n">decoder</span><span class="p">:</span> <span class="n">decoder</span><span class="p">,</span> <span class="k">for</span><span class="p">:</span> <span class="p">.</span><span class="n">json</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>You should call this function from <code>configure.swift</code> like this:</p>
|
||
<div class="codehilite"><pre><span></span> <span class="c1">/// Register Content Config</span>
|
||
<span class="kd">var</span> <span class="nv">contentConfig</span> <span class="p">=</span> <span class="n">ContentConfig</span><span class="p">.</span><span class="k">default</span><span class="p">()</span>
|
||
<span class="k">try</span> <span class="n">content</span><span class="p">(</span><span class="n">config</span><span class="p">:</span> <span class="p">&</span><span class="n">contentConfig</span><span class="p">)</span>
|
||
<span class="n">services</span><span class="p">.</span><span class="n">register</span><span class="p">(</span><span class="n">contentConfig</span><span class="p">)</span>
|
||
</pre></div>
|
||
|
||
|
||
<blockquote>
|
||
<p>If you don't customize the content configuration you can omit this file.</p>
|
||
</blockquote>
|
||
<h4 id="databasesswift">databases.swift<a class="headerlink" href="#databasesswift" title="Permanent link">¶</a></h4>
|
||
<p>Use this file to add the databases used in your application. Extracting this logic to a separate file keeps the configure.swift code clean, as it can often get quite long. This file should look something like this:</p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">import</span> <span class="nc">Vapor</span>
|
||
<span class="kd">import</span> <span class="nc">FluentMySQL</span> <span class="c1">//use your database driver here</span>
|
||
|
||
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">databases</span><span class="p">(</span><span class="n">config</span><span class="p">:</span> <span class="kr">inout</span> <span class="n">DatabasesConfig</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">{</span>
|
||
<span class="k">guard</span> <span class="kd">let</span> <span class="nv">databaseUrl</span> <span class="p">=</span> <span class="n">Environment</span><span class="p">.</span><span class="kr">get</span><span class="p">(</span><span class="s">"DATABASE_URL"</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span>
|
||
<span class="k">throw</span> <span class="n">Abort</span><span class="p">(.</span><span class="n">internalServerError</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="k">guard</span> <span class="kd">let</span> <span class="nv">dbConfig</span> <span class="p">=</span> <span class="n">MySQLDatabaseConfig</span><span class="p">(</span><span class="n">url</span><span class="p">:</span> <span class="n">databaseUrl</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">throw</span> <span class="n">Abort</span><span class="p">(.</span><span class="n">internalServerError</span><span class="p">)</span> <span class="p">}</span>
|
||
|
||
<span class="c1">/// Register the databases</span>
|
||
<span class="n">config</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">database</span><span class="p">:</span> <span class="n">MySQLDatabase</span><span class="p">(</span><span class="n">config</span><span class="p">:</span> <span class="n">dbConfig</span><span class="p">),</span> <span class="k">as</span><span class="p">:</span> <span class="p">.</span><span class="n">mysql</span><span class="p">)</span>
|
||
|
||
<span class="p">...</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>And then call this function from <code>configure.swift</code> like this:</p>
|
||
<div class="codehilite"><pre><span></span> <span class="c1">/// Register the configured SQLite database to the database config.</span>
|
||
<span class="kd">var</span> <span class="nv">databasesConfig</span> <span class="p">=</span> <span class="n">DatabasesConfig</span><span class="p">()</span>
|
||
<span class="k">try</span> <span class="n">databases</span><span class="p">(</span><span class="n">config</span><span class="p">:</span> <span class="p">&</span><span class="n">databasesConfig</span><span class="p">)</span>
|
||
<span class="n">services</span><span class="p">.</span><span class="n">register</span><span class="p">(</span><span class="n">databasesConfig</span><span class="p">)</span>
|
||
</pre></div>
|
||
|
||
|
||
<blockquote>
|
||
<p>If your app doesn't use <code>Fluent</code> you can omit this file.</p>
|
||
</blockquote>
|
||
<h4 id="middlewaresswift">middlewares.swift<a class="headerlink" href="#middlewaresswift" title="Permanent link">¶</a></h4>
|
||
<p>In this file you can customize the middlewares of your application. For example:</p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">import</span> <span class="nc">Vapor</span>
|
||
|
||
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">middlewares</span><span class="p">(</span><span class="n">config</span><span class="p">:</span> <span class="kr">inout</span> <span class="n">MiddlewareConfig</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">{</span>
|
||
<span class="c1">// config.use(FileMiddleware.self) // Serves files from `Public/` directory</span>
|
||
<span class="n">config</span><span class="p">.</span><span class="n">use</span><span class="p">(</span><span class="n">ErrorMiddleware</span><span class="p">.</span><span class="kc">self</span><span class="p">)</span> <span class="c1">// Catches errors and converts to HTTP response</span>
|
||
<span class="c1">// Other Middlewares...</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>You should call this function from <code>configure.swift</code> like this:</p>
|
||
<div class="codehilite"><pre><span></span> <span class="c1">/// Register middlewares</span>
|
||
<span class="kd">var</span> <span class="nv">middlewaresConfig</span> <span class="p">=</span> <span class="n">MiddlewareConfig</span><span class="p">()</span>
|
||
<span class="k">try</span> <span class="n">middlewares</span><span class="p">(</span><span class="n">config</span><span class="p">:</span> <span class="p">&</span><span class="n">middlewaresConfig</span><span class="p">)</span>
|
||
<span class="n">services</span><span class="p">.</span><span class="n">register</span><span class="p">(</span><span class="n">middlewaresConfig</span><span class="p">)</span>
|
||
</pre></div>
|
||
|
||
|
||
<h4 id="migrateswift">migrate.swift<a class="headerlink" href="#migrateswift" title="Permanent link">¶</a></h4>
|
||
<p>Use this file to add the migrations to your database. Extracting this logic to a separate file keeps the configure.swift code clean, as it can often get quite long. This file should look something like this:</p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">import</span> <span class="nc">Vapor</span>
|
||
<span class="kd">import</span> <span class="nc">FluentMySQL</span> <span class="c1">//use your database driver here</span>
|
||
|
||
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">migrate</span><span class="p">(</span><span class="n">migrations</span><span class="p">:</span> <span class="kr">inout</span> <span class="n">MigrationConfig</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">{</span>
|
||
<span class="n">migrations</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">model</span><span class="p">:</span> <span class="n">User</span><span class="p">.</span><span class="kc">self</span><span class="p">,</span> <span class="n">database</span><span class="p">:</span> <span class="p">.</span><span class="n">mysql</span><span class="p">)</span> <span class="c1">//update this with your database driver</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>And then call this function from <code>configure.swift</code> like this:</p>
|
||
<div class="codehilite"><pre><span></span> <span class="n">services</span><span class="p">.</span><span class="n">register</span> <span class="p">{</span> <span class="n">container</span> <span class="p">-></span> <span class="n">MigrationConfig</span> <span class="k">in</span>
|
||
<span class="kd">var</span> <span class="nv">migrationConfig</span> <span class="p">=</span> <span class="n">MigrationConfig</span><span class="p">()</span>
|
||
<span class="k">try</span> <span class="n">migrate</span><span class="p">(</span><span class="n">migrations</span><span class="p">:</span> <span class="p">&</span><span class="n">migrationConfig</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">migrationConfig</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>As you continue to add models to your application, make sure that you add them to the migration file as well.</p>
|
||
<blockquote>
|
||
<p>If your app doesn't use <code>Fluent</code> you can omit this file.</p>
|
||
</blockquote>
|
||
<h4 id="repositoriesswift">repositories.swift<a class="headerlink" href="#repositoriesswift" title="Permanent link">¶</a></h4>
|
||
<p>The <code>repositories.swift</code> file is responsible for registering each repository during the configuration stage. This file should look like this:</p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">import</span> <span class="nc">Vapor</span>
|
||
|
||
<span class="kd">public</span> <span class="kd">func</span> <span class="nf">setupRepositories</span><span class="p">(</span><span class="n">services</span><span class="p">:</span> <span class="kr">inout</span> <span class="n">Services</span><span class="p">,</span> <span class="n">config</span><span class="p">:</span> <span class="kr">inout</span> <span class="n">Config</span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="n">services</span><span class="p">.</span><span class="n">register</span><span class="p">(</span><span class="n">MySQLUserRepository</span><span class="p">.</span><span class="kc">self</span><span class="p">)</span>
|
||
<span class="n">preferDatabaseRepositories</span><span class="p">(</span><span class="n">config</span><span class="p">:</span> <span class="p">&</span><span class="n">config</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">private</span> <span class="kd">func</span> <span class="nf">preferDatabaseRepositories</span><span class="p">(</span><span class="n">config</span><span class="p">:</span> <span class="kr">inout</span> <span class="n">Config</span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="n">config</span><span class="p">.</span><span class="n">prefer</span><span class="p">(</span><span class="n">MySQLUserRepository</span><span class="p">.</span><span class="kc">self</span><span class="p">,</span> <span class="k">for</span><span class="p">:</span> <span class="n">UserRepository</span><span class="p">.</span><span class="kc">self</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Call this function from <code>configure.swift</code> like this:</p>
|
||
<div class="codehilite"><pre><span></span><span class="n">setupRepositories</span><span class="p">(</span><span class="n">services</span><span class="p">:</span> <span class="p">&</span><span class="n">services</span><span class="p">,</span> <span class="n">config</span><span class="p">:</span> <span class="p">&</span><span class="n">config</span><span class="p">)</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>For more information on the repository pattern, see the “Architecture” section.</p>
|
||
<blockquote>
|
||
<p>If your app doesn't use <code>Fluent</code> you can omit this file.</p>
|
||
</blockquote>
|
||
<h2 id="credentials">Credentials<a class="headerlink" href="#credentials" title="Permanent link">¶</a></h2>
|
||
<p>Credentials are a crucial part to any production-ready application. The preferred way to manage secrets in a Vapor application is via environment variables. These variables can be set via the Xcode scheme editor for testing, the shell, or in the GUI of your hosting provider.</p>
|
||
<p><strong>Credentials should never, under any circumstances, be checked into a source control repository.</strong></p>
|
||
<p>Assuming we have the following credential storage service:</p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">import</span> <span class="nc">Vapor</span>
|
||
<span class="kd">struct</span> <span class="nc">APIKeyStorage</span><span class="p">:</span> <span class="n">Service</span> <span class="p">{</span>
|
||
<span class="kd">let</span> <span class="nv">apiKey</span><span class="p">:</span> <span class="nb">String</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="n">services</span><span class="p">.</span><span class="n">register</span> <span class="p">{</span> <span class="n">container</span> <span class="p">-></span> <span class="n">APIKeyStorage</span> <span class="k">in</span>
|
||
<span class="k">return</span> <span class="n">APIKeyStorage</span><span class="p">(</span><span class="n">apiKey</span><span class="p">:</span> <span class="s">"MY-SUPER-SECRET-API-KEY"</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="k">guard</span> <span class="kd">let</span> <span class="nv">apiKey</span> <span class="p">=</span> <span class="n">Environment</span><span class="p">.</span><span class="kr">get</span><span class="p">(</span><span class="s">"api-key"</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">throw</span> <span class="n">Abort</span><span class="p">(.</span><span class="n">internalServerError</span><span class="p">)</span> <span class="p">}</span>
|
||
<span class="n">services</span><span class="p">.</span><span class="n">register</span> <span class="p">{</span> <span class="n">container</span> <span class="p">-></span> <span class="n">APIKeyStorage</span> <span class="k">in</span>
|
||
<span class="k">return</span> <span class="n">APIKeyStorage</span><span class="p">(</span><span class="n">apiKey</span><span class="p">:</span> <span class="n">apiKey</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<h2 id="file-naming">File Naming<a class="headerlink" href="#file-naming" title="Permanent link">¶</a></h2>
|
||
<p>As the old saying goes, “the two hardest problems in computer science are naming things, cache invalidation, and off by one errors.” To minimize confusion and help increase readability, files should be named succinctly and descriptively.</p>
|
||
<p>Files that contain objects used to decode body content from a request should be appended with <code>Request</code>. For example, <code>LoginRequest</code>. Files that contain objects used to encode body content to a response should be appended with <code>Response</code>. For example, <code>LoginResponse</code>.</p>
|
||
<p>Controllers should also be named descriptively for their purpose. If your application contains logic for frontend responses and API responses, each controller’s name should denote their responsibility. For example, <code>LoginViewController</code> and <code>LoginController</code>. If you combine the login functionality into one controller, opt for the more generic name: <code>LoginController</code>.</p>
|
||
<h2 id="architecture">Architecture<a class="headerlink" href="#architecture" title="Permanent link">¶</a></h2>
|
||
<p>One of the most important decisions to make up front about your app is the style of architecture it will follow. It is incredibly time consuming and expensive to retroactively change your architecture. We recommend that production-level Vapor applications use the repository pattern.</p>
|
||
<p>The basic idea behind the repository pattern is that it creates another abstraction between Fluent and your application code. Instead of using Fluent queries directly in controllers, this pattern encourages abstracting those queries into a more generic protocol and using that instead.</p>
|
||
<p>There are a few benefits to this method. First, it makes testing a lot easier. This is because during the test environment you can easily utilize Vapor’s configuration abilities to swap out which implementation of the repository protocol gets used. This makes unit testing much faster because the unit tests can use a memory version of the protocol rather than the database. The other large benefit to this pattern is that it makes it really easy to switch out the database layer if needed. Because all of the ORM logic is abstracted to this piece of the application (and the controllers don’t know it exists) you could realistically swap out Fluent with a different ORM with minimal changes to your actual application/business logic code. This also means that you could switch out the specific database used during testing (i.e. using SQLite instead of MySQL). However, this strategy requires extra work to make your models generic as they can no longer conform the the specific database model types (e.g. <code>MySQLModel</code>). </p>
|
||
<p>Here’s an example of a <code>UserRepository</code>:</p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">import</span> <span class="nc">Vapor</span>
|
||
<span class="kd">import</span> <span class="nc">FluentMySQL</span>
|
||
<span class="kd">import</span> <span class="nc">Foundation</span>
|
||
|
||
<span class="kd">protocol</span> <span class="nc">UserRepository</span><span class="p">:</span> <span class="n">ServiceType</span> <span class="p">{</span>
|
||
<span class="kd">func</span> <span class="nf">find</span><span class="p">(</span><span class="n">id</span><span class="p">:</span> <span class="nb">Int</span><span class="p">)</span> <span class="p">-></span> <span class="n">Future</span><span class="p"><</span><span class="n">User</span><span class="p">?</span><span class="o">></span>
|
||
<span class="kd">func</span> <span class="nf">all</span><span class="p">()</span> <span class="p">-></span> <span class="n">Future</span><span class="o"><</span><span class="p">[</span><span class="n">User</span><span class="p">]</span><span class="o">></span>
|
||
<span class="kd">func</span> <span class="nf">find</span><span class="p">(</span><span class="n">email</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="p">-></span> <span class="n">Future</span><span class="p"><</span><span class="n">User</span><span class="p">?</span><span class="o">></span>
|
||
<span class="kd">func</span> <span class="nf">findCount</span><span class="p">(</span><span class="n">email</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="p">-></span> <span class="n">Future</span><span class="p"><</span><span class="nb">Int</span><span class="p">></span>
|
||
<span class="kd">func</span> <span class="nf">save</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span><span class="p">)</span> <span class="p">-></span> <span class="n">Future</span><span class="p"><</span><span class="n">User</span><span class="p">></span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kr">final</span> <span class="kd">class</span> <span class="nc">MySQLUserRepository</span><span class="p">:</span> <span class="n">UserRepository</span> <span class="p">{</span>
|
||
<span class="kd">let</span> <span class="nv">db</span><span class="p">:</span> <span class="n">MySQLDatabase</span><span class="p">.</span><span class="n">ConnectionPool</span>
|
||
|
||
<span class="kd">init</span><span class="p">(</span><span class="kc">_</span> <span class="n">db</span><span class="p">:</span> <span class="n">MySQLDatabase</span><span class="p">.</span><span class="n">ConnectionPool</span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="kc">self</span><span class="p">.</span><span class="n">db</span> <span class="p">=</span> <span class="n">db</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">func</span> <span class="nf">find</span><span class="p">(</span><span class="n">id</span><span class="p">:</span> <span class="nb">Int</span><span class="p">)</span> <span class="p">-></span> <span class="n">EventLoopFuture</span><span class="p"><</span><span class="n">User</span><span class="p">?</span><span class="o">></span> <span class="p">{</span>
|
||
<span class="k">return</span> <span class="n">db</span><span class="p">.</span><span class="n">withConnection</span> <span class="p">{</span> <span class="n">conn</span> <span class="k">in</span>
|
||
<span class="k">return</span> <span class="n">User</span><span class="p">.</span><span class="bp">find</span><span class="p">(</span><span class="n">id</span><span class="p">,</span> <span class="n">on</span><span class="p">:</span> <span class="n">conn</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">func</span> <span class="nf">all</span><span class="p">()</span> <span class="p">-></span> <span class="n">EventLoopFuture</span><span class="o"><</span><span class="p">[</span><span class="n">User</span><span class="p">]</span><span class="o">></span> <span class="p">{</span>
|
||
<span class="k">return</span> <span class="n">db</span><span class="p">.</span><span class="n">withConnection</span> <span class="p">{</span> <span class="n">conn</span> <span class="k">in</span>
|
||
<span class="k">return</span> <span class="n">User</span><span class="p">.</span><span class="n">query</span><span class="p">(</span><span class="n">on</span><span class="p">:</span> <span class="n">conn</span><span class="p">).</span><span class="n">all</span><span class="p">()</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">func</span> <span class="nf">find</span><span class="p">(</span><span class="n">email</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="p">-></span> <span class="n">EventLoopFuture</span><span class="p"><</span><span class="n">User</span><span class="p">?</span><span class="o">></span> <span class="p">{</span>
|
||
<span class="k">return</span> <span class="n">db</span><span class="p">.</span><span class="n">withConnection</span> <span class="p">{</span> <span class="n">conn</span> <span class="k">in</span>
|
||
<span class="k">return</span> <span class="n">User</span><span class="p">.</span><span class="n">query</span><span class="p">(</span><span class="n">on</span><span class="p">:</span> <span class="n">conn</span><span class="p">).</span><span class="bp">filter</span><span class="p">(</span><span class="err">\</span><span class="p">.</span><span class="n">email</span> <span class="p">==</span> <span class="n">email</span><span class="p">).</span><span class="bp">first</span><span class="p">()</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">func</span> <span class="nf">findCount</span><span class="p">(</span><span class="n">email</span><span class="p">:</span> <span class="nb">String</span><span class="p">)</span> <span class="p">-></span> <span class="n">EventLoopFuture</span><span class="p"><</span><span class="nb">Int</span><span class="p">></span> <span class="p">{</span>
|
||
<span class="k">return</span> <span class="n">db</span><span class="p">.</span><span class="n">withConnection</span> <span class="p">{</span> <span class="n">conn</span> <span class="k">in</span>
|
||
<span class="k">return</span> <span class="n">User</span><span class="p">.</span><span class="n">query</span><span class="p">(</span><span class="n">on</span><span class="p">:</span> <span class="n">conn</span><span class="p">).</span><span class="bp">filter</span><span class="p">(</span><span class="err">\</span><span class="p">.</span><span class="n">email</span> <span class="p">==</span> <span class="n">email</span><span class="p">).</span><span class="bp">count</span><span class="p">()</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">func</span> <span class="nf">save</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="n">User</span><span class="p">)</span> <span class="p">-></span> <span class="n">EventLoopFuture</span><span class="p"><</span><span class="n">User</span><span class="p">></span> <span class="p">{</span>
|
||
<span class="k">return</span> <span class="n">db</span><span class="p">.</span><span class="n">withConnection</span> <span class="p">{</span> <span class="n">conn</span> <span class="k">in</span>
|
||
<span class="k">return</span> <span class="n">user</span><span class="p">.</span><span class="n">save</span><span class="p">(</span><span class="n">on</span><span class="p">:</span> <span class="n">conn</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="c1">//</span><span class="cs">MARK:</span><span class="c1"> - ServiceType conformance</span>
|
||
<span class="kd">extension</span> <span class="nc">MySQLUserRepository</span> <span class="p">{</span>
|
||
<span class="kd">static</span> <span class="kd">let</span> <span class="nv">serviceSupports</span><span class="p">:</span> <span class="p">[</span><span class="nb">Any</span><span class="p">.</span><span class="kr">Type</span><span class="p">]</span> <span class="p">=</span> <span class="p">[</span><span class="n">UserRepository</span><span class="p">.</span><span class="kc">self</span><span class="p">]</span>
|
||
|
||
<span class="kd">static</span> <span class="kd">func</span> <span class="nf">makeService</span><span class="p">(</span><span class="k">for</span> <span class="n">worker</span><span class="p">:</span> <span class="n">Container</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">-></span> <span class="kc">Self</span> <span class="p">{</span>
|
||
<span class="k">return</span> <span class="p">.</span><span class="kd">init</span><span class="p">(</span><span class="k">try</span> <span class="n">worker</span><span class="p">.</span><span class="n">connectionPool</span><span class="p">(</span><span class="n">to</span><span class="p">:</span> <span class="p">.</span><span class="n">mysql</span><span class="p">))</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">extension</span> <span class="nc">Database</span> <span class="p">{</span>
|
||
<span class="kd">public</span> <span class="kd">typealias</span> <span class="n">ConnectionPool</span> <span class="p">=</span> <span class="n">DatabaseConnectionPool</span><span class="p"><</span><span class="n">ConfiguredDatabase</span><span class="p"><</span><span class="kc">Self</span><span class="p">></span><span class="o">></span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Then, in the controller:</p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">let</span> <span class="nv">repository</span> <span class="p">=</span> <span class="k">try</span> <span class="n">req</span><span class="p">.</span><span class="n">make</span><span class="p">(</span><span class="n">UserRepository</span><span class="p">.</span><span class="kc">self</span><span class="p">)</span>
|
||
<span class="kd">let</span> <span class="nv">userQuery</span> <span class="p">=</span> <span class="n">repository</span>
|
||
<span class="p">.</span><span class="bp">find</span><span class="p">(</span><span class="n">email</span><span class="p">:</span> <span class="n">content</span><span class="p">.</span><span class="n">email</span><span class="p">)</span>
|
||
<span class="p">.</span><span class="n">unwrap</span><span class="p">(</span><span class="n">or</span><span class="p">:</span> <span class="n">Abort</span><span class="p">(.</span><span class="n">unauthorized</span><span class="p">,</span> <span class="n">reason</span><span class="p">:</span> <span class="s">"Invalid Credentials"</span><span class="p">))</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>In this example, the controller has no idea where the data is coming from, it only knows that it exists. This model has proven to be incredibly effective with Vapor and it is our recommended architecture.</p>
|
||
<h2 id="entities">Entities<a class="headerlink" href="#entities" title="Permanent link">¶</a></h2>
|
||
<p>Oftentimes entities that come from the database layer need to be transformed to make them appropriate for a JSON response or for sending to the view layer. Sometimes these data transformations require database queries as well. If the transformation is simple, use a property and not a function.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">func</span> <span class="nf">publicUser</span><span class="p">()</span> <span class="p">-></span> <span class="n">PublicUser</span> <span class="p">{</span>
|
||
<span class="k">return</span> <span class="n">PublicUser</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="kc">self</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">var</span> <span class="p">`</span><span class="kd">public</span><span class="p">`:</span> <span class="n">PublicUser</span> <span class="p">{</span>
|
||
<span class="k">return</span> <span class="n">PublicUser</span><span class="p">(</span><span class="n">user</span><span class="p">:</span> <span class="kc">self</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Transformations that require more complex processing (fetching siblings and add them to the object) should be functions that accept a DatabaseConnectable object:</p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">func</span> <span class="nf">userWithSiblings</span><span class="p">(</span><span class="n">on</span> <span class="n">connectable</span><span class="p">:</span> <span class="n">DatabaseConnectable</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">-></span> <span class="n">Future</span><span class="p"><</span><span class="n">FullUser</span><span class="p">></span> <span class="p">{</span>
|
||
<span class="c1">//do the processing here</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<hr/>
|
||
|
||
<p>We also recommend documenting all functions that exist on entities.</p>
|
||
<p>Unless your entity needs to be database-generic, always conform the model to the most specific model type.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">extension</span> <span class="nc">User</span><span class="p">:</span> <span class="n">Model</span> <span class="p">{</span> <span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">extension</span> <span class="nc">User</span><span class="p">:</span> <span class="n">MySQLModel</span> <span class="p">{</span> <span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<hr/>
|
||
|
||
<p>Extending the model with other conformances (Migration, Parameter, etc) should be done at the file scope via an extension.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">public</span> <span class="kr">final</span> <span class="kd">class</span> <span class="nc">User</span><span class="p">:</span> <span class="n">Model</span><span class="p">,</span> <span class="n">Parameter</span><span class="p">,</span> <span class="n">Content</span><span class="p">,</span> <span class="n">Migration</span> <span class="p">{</span>
|
||
<span class="c1">//..</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">public</span> <span class="kr">final</span> <span class="kd">class</span> <span class="nc">User</span> <span class="p">{</span>
|
||
<span class="c1">//..</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">extension</span> <span class="nc">User</span><span class="p">:</span> <span class="n">MySQLModel</span> <span class="p">{</span> <span class="p">}</span>
|
||
<span class="kd">extension</span> <span class="nc">User</span><span class="p">:</span> <span class="n">Parameter</span> <span class="p">{</span> <span class="p">}</span>
|
||
<span class="kd">extension</span> <span class="nc">User</span><span class="p">:</span> <span class="n">Migration</span> <span class="p">{</span> <span class="p">}</span>
|
||
<span class="kd">extension</span> <span class="nc">User</span><span class="p">:</span> <span class="n">Content</span> <span class="p">{</span> <span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<hr/>
|
||
|
||
<p>Property naming styles should remain consistent throughout all models.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">public</span> <span class="kr">final</span> <span class="kd">class</span> <span class="nc">User</span> <span class="p">{</span>
|
||
<span class="kd">var</span> <span class="nv">id</span><span class="p">:</span> <span class="nb">Int</span><span class="p">?</span>
|
||
<span class="kd">var</span> <span class="nv">firstName</span><span class="p">:</span> <span class="nb">String</span>
|
||
<span class="kd">var</span> <span class="nv">last_name</span><span class="p">:</span> <span class="nb">String</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">public</span> <span class="kr">final</span> <span class="kd">class</span> <span class="nc">User</span> <span class="p">{</span>
|
||
<span class="kd">var</span> <span class="nv">id</span><span class="p">:</span> <span class="nb">Int</span><span class="p">?</span>
|
||
<span class="kd">var</span> <span class="nv">firstName</span><span class="p">:</span> <span class="nb">String</span>
|
||
<span class="kd">var</span> <span class="nv">lastName</span><span class="p">:</span> <span class="nb">String</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>As a general rule, try to abstract logic into functions on the models to keep the controllers clean.</p>
|
||
<h2 id="routes-and-controllers">Routes and Controllers<a class="headerlink" href="#routes-and-controllers" title="Permanent link">¶</a></h2>
|
||
<p>We suggest combining your routes into your controller to keep everything central. Controllers serve as a jumping off point for executing logic from other places, namely repositories and model functions.</p>
|
||
<p>Routes should be separated into functions in the controller that take a <code>Request</code> parameter and return a <code>ResponseEncodable</code> type.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kr">final</span> <span class="kd">class</span> <span class="nc">LoginViewController</span><span class="p">:</span> <span class="n">RouteCollection</span> <span class="p">{</span>
|
||
<span class="kd">func</span> <span class="nf">boot</span><span class="p">(</span><span class="n">router</span><span class="p">:</span> <span class="n">Router</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">{</span>
|
||
<span class="n">router</span><span class="p">.</span><span class="kr">get</span><span class="p">(</span><span class="s">"/login"</span><span class="p">)</span> <span class="p">{</span> <span class="p">(</span><span class="n">req</span><span class="p">)</span> <span class="p">-></span> <span class="n">ResponseEncodable</span> <span class="k">in</span>
|
||
<span class="k">return</span> <span class="s">""</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kr">final</span> <span class="kd">class</span> <span class="nc">LoginViewController</span><span class="p">:</span> <span class="n">RouteCollection</span> <span class="p">{</span>
|
||
<span class="kd">func</span> <span class="nf">boot</span><span class="p">(</span><span class="n">router</span><span class="p">:</span> <span class="n">Router</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">{</span>
|
||
<span class="n">router</span><span class="p">.</span><span class="kr">get</span><span class="p">(</span><span class="s">"/login"</span><span class="p">,</span> <span class="n">use</span><span class="p">:</span> <span class="n">login</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">func</span> <span class="nf">login</span><span class="p">(</span><span class="n">req</span><span class="p">:</span> <span class="n">Request</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">-></span> <span class="nb">String</span> <span class="p">{</span>
|
||
<span class="k">return</span> <span class="s">""</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<hr/>
|
||
|
||
<p>When creating these route functions, the return type should always be as specific as possible.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">func</span> <span class="nf">login</span><span class="p">(</span><span class="n">req</span><span class="p">:</span> <span class="n">Request</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">-></span> <span class="n">ResponseEncodable</span> <span class="p">{</span>
|
||
<span class="k">return</span> <span class="s">"string"</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">func</span> <span class="nf">login</span><span class="p">(</span><span class="n">req</span><span class="p">:</span> <span class="n">Request</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">-></span> <span class="nb">String</span> <span class="p">{</span>
|
||
<span class="k">return</span> <span class="s">"string"</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<hr/>
|
||
|
||
<p>When creating a path like <code>/user/:userId</code>, always use the most specific <code>Parameter</code> instance available.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="n">router</span><span class="p">.</span><span class="kr">get</span><span class="p">(</span><span class="s">"/user"</span><span class="p">,</span> <span class="nb">Int</span><span class="p">.</span><span class="n">parameter</span><span class="p">,</span> <span class="n">use</span><span class="p">:</span> <span class="n">user</span><span class="p">)</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="n">router</span><span class="p">.</span><span class="kr">get</span><span class="p">(</span><span class="s">"/user"</span><span class="p">,</span> <span class="n">User</span><span class="p">.</span><span class="n">parameter</span><span class="p">,</span> <span class="n">use</span><span class="p">:</span> <span class="n">user</span><span class="p">)</span>
|
||
</pre></div>
|
||
|
||
|
||
<hr/>
|
||
|
||
<p>When decoding a request, opt to decode the <code>Content</code> object when registering the route instead of in the route.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="n">router</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="s">"/update"</span><span class="p">,</span> <span class="n">use</span><span class="p">:</span> <span class="n">update</span><span class="p">)</span>
|
||
|
||
<span class="kd">func</span> <span class="nf">update</span><span class="p">(</span><span class="n">req</span><span class="p">:</span> <span class="n">Request</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">-></span> <span class="n">Future</span><span class="p"><</span><span class="n">User</span><span class="p">></span> <span class="p">{</span>
|
||
<span class="k">return</span> <span class="n">req</span><span class="p">.</span><span class="n">content</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="n">User</span><span class="p">.</span><span class="kc">self</span><span class="p">).</span><span class="bp">map</span> <span class="p">{</span> <span class="n">user</span> <span class="k">in</span>
|
||
<span class="c1">//do something with user</span>
|
||
|
||
<span class="k">return</span> <span class="n">user</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="n">router</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="n">User</span><span class="p">.</span><span class="kc">self</span><span class="p">,</span> <span class="n">at</span><span class="p">:</span> <span class="s">"/update"</span><span class="p">,</span> <span class="n">use</span><span class="p">:</span> <span class="n">update</span><span class="p">)</span>
|
||
|
||
<span class="kd">func</span> <span class="nf">update</span><span class="p">(</span><span class="n">req</span><span class="p">:</span> <span class="n">Request</span><span class="p">,</span> <span class="n">content</span><span class="p">:</span> <span class="n">User</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">-></span> <span class="n">Future</span><span class="p"><</span><span class="n">User</span><span class="p">></span> <span class="p">{</span>
|
||
<span class="k">return</span> <span class="n">content</span><span class="p">.</span><span class="n">save</span><span class="p">(</span><span class="n">on</span><span class="p">:</span> <span class="n">req</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Controllers should follow the thread-safe architecture when possible. This means passing necessary <code>Service</code>s to the controller on initialization instead of making them in the routes. </p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kr">final</span> <span class="kd">class</span> <span class="nc">LoginViewController</span><span class="p">:</span> <span class="n">RouteCollection</span> <span class="p">{</span>
|
||
<span class="kd">func</span> <span class="nf">boot</span><span class="p">(</span><span class="n">router</span><span class="p">:</span> <span class="n">Router</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">{</span>
|
||
<span class="n">router</span><span class="p">.</span><span class="kr">get</span><span class="p">(</span><span class="s">"/login"</span><span class="p">,</span> <span class="n">use</span><span class="p">:</span> <span class="n">login</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">func</span> <span class="nf">login</span><span class="p">(</span><span class="n">req</span><span class="p">:</span> <span class="n">Request</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">-></span> <span class="nb">String</span> <span class="p">{</span>
|
||
<span class="kd">let</span> <span class="nv">userRepository</span> <span class="p">=</span> <span class="k">try</span> <span class="n">req</span><span class="p">.</span><span class="n">make</span><span class="p">(</span><span class="n">UserRepository</span><span class="p">.</span><span class="kc">self</span><span class="p">)</span>
|
||
<span class="c1">//do something with it </span>
|
||
|
||
<span class="k">return</span> <span class="s">""</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kr">final</span> <span class="kd">class</span> <span class="nc">LoginViewController</span><span class="p">:</span> <span class="n">RouteCollection</span> <span class="p">{</span>
|
||
<span class="kd">private</span> <span class="kd">let</span> <span class="nv">userRepository</span><span class="p">:</span> <span class="n">UserRepository</span>
|
||
|
||
<span class="kd">init</span><span class="p">(</span><span class="n">userRepository</span><span class="p">:</span> <span class="n">UserRepository</span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="kc">self</span><span class="p">.</span><span class="n">userRepository</span> <span class="p">=</span> <span class="n">userRepository</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">func</span> <span class="nf">boot</span><span class="p">(</span><span class="n">router</span><span class="p">:</span> <span class="n">Router</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">{</span>
|
||
<span class="n">router</span><span class="p">.</span><span class="kr">get</span><span class="p">(</span><span class="s">"/login"</span><span class="p">,</span> <span class="n">use</span><span class="p">:</span> <span class="n">login</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">func</span> <span class="nf">login</span><span class="p">(</span><span class="n">req</span><span class="p">:</span> <span class="n">Request</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">-></span> <span class="nb">String</span> <span class="p">{</span>
|
||
<span class="c1">//use `self.userRepository`</span>
|
||
|
||
<span class="k">return</span> <span class="s">""</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Controllers should only cover one idea/feature at a time. If a feature grows to encapsulate a large amount of functionality, routes should be split up into multiple controllers and organized under one common feature folder in the <code>Controllers</code> folder. For example, an app that handles generating a lot of analytical/reporting views should break up the logic by specific report to avoid cluttering a generic <code>ReportsViewController.swift</code></p>
|
||
<h2 id="async">Async<a class="headerlink" href="#async" title="Permanent link">¶</a></h2>
|
||
<p>Where possible, avoid specifying the type information in flatMap and map calls.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">let</span> <span class="nv">stringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
<span class="k">return</span> <span class="n">stringFuture</span><span class="p">.</span><span class="bp">map</span><span class="p">(</span><span class="n">to</span><span class="p">:</span> <span class="n">Response</span><span class="p">.</span><span class="kc">self</span><span class="p">)</span> <span class="p">{</span> <span class="n">string</span> <span class="k">in</span>
|
||
<span class="k">return</span> <span class="n">req</span><span class="p">.</span><span class="n">redirect</span><span class="p">(</span><span class="n">to</span><span class="p">:</span> <span class="n">string</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">let</span> <span class="nv">stringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
<span class="k">return</span> <span class="n">stringFuture</span><span class="p">.</span><span class="bp">map</span> <span class="p">{</span> <span class="n">string</span> <span class="k">in</span>
|
||
<span class="k">return</span> <span class="n">req</span><span class="p">.</span><span class="n">redirect</span><span class="p">(</span><span class="n">to</span><span class="p">:</span> <span class="n">string</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<hr/>
|
||
|
||
<p>When returning two objects from a chain to the next chain, use the <code>and(result: )</code> function to automatically create a tuple instead of manually creating it (the Swift compiler will most likely require return type information in this case)</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">let</span> <span class="nv">stringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
<span class="k">return</span> <span class="n">stringFuture</span><span class="p">.</span><span class="n">flatMap</span><span class="p">(</span><span class="n">to</span><span class="p">:</span> <span class="p">(</span><span class="nb">String</span><span class="p">,</span> <span class="nb">String</span><span class="p">).</span><span class="kc">self</span><span class="p">)</span> <span class="p">{</span> <span class="n">original</span> <span class="k">in</span>
|
||
<span class="kd">let</span> <span class="nv">otherStringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
|
||
<span class="k">return</span> <span class="n">otherStringFuture</span><span class="p">.</span><span class="bp">map</span> <span class="p">{</span> <span class="n">other</span> <span class="k">in</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">original</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}.</span><span class="bp">map</span> <span class="p">{</span> <span class="n">other</span><span class="p">,</span> <span class="n">original</span> <span class="k">in</span>
|
||
<span class="c1">//do something</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">let</span> <span class="nv">stringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
<span class="k">return</span> <span class="n">stringFuture</span><span class="p">.</span><span class="n">flatMap</span><span class="p">(</span><span class="n">to</span><span class="p">:</span> <span class="p">(</span><span class="nb">String</span><span class="p">,</span> <span class="nb">String</span><span class="p">).</span><span class="kc">self</span><span class="p">)</span> <span class="p">{</span> <span class="n">original</span> <span class="k">in</span>
|
||
<span class="kd">let</span> <span class="nv">otherStringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
<span class="k">return</span> <span class="n">otherStringFuture</span><span class="p">.</span><span class="n">and</span><span class="p">(</span><span class="n">result</span><span class="p">:</span> <span class="n">original</span><span class="p">)</span>
|
||
<span class="p">}.</span><span class="bp">map</span> <span class="p">{</span> <span class="n">other</span><span class="p">,</span> <span class="n">original</span> <span class="k">in</span>
|
||
<span class="c1">//do something</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<hr/>
|
||
|
||
<p>When returning more than two objects from one chain to the next, do not rely on the <code>and(result )</code> method as it can only create, at most, a two object tuple. Use a nested <code>map</code> instead.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">let</span> <span class="nv">stringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
<span class="kd">let</span> <span class="nv">secondFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
|
||
<span class="k">return</span> <span class="n">flatMap</span><span class="p">(</span><span class="n">to</span><span class="p">:</span> <span class="p">(</span><span class="nb">String</span><span class="p">,</span> <span class="p">(</span><span class="nb">String</span><span class="p">,</span> <span class="nb">String</span><span class="p">)).</span><span class="kc">self</span><span class="p">,</span> <span class="n">stringFuture</span><span class="p">,</span> <span class="n">secondFuture</span><span class="p">)</span> <span class="p">{</span> <span class="bp">first</span><span class="p">,</span> <span class="n">second</span> <span class="k">in</span>
|
||
<span class="kd">let</span> <span class="nv">thirdFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
<span class="k">return</span> <span class="n">thirdFuture</span><span class="p">.</span><span class="n">and</span><span class="p">(</span><span class="n">result</span><span class="p">:</span> <span class="p">(</span><span class="bp">first</span><span class="p">,</span> <span class="n">second</span><span class="p">))</span>
|
||
<span class="p">}.</span><span class="bp">map</span> <span class="p">{</span> <span class="n">other</span><span class="p">,</span> <span class="n">firstSecondTuple</span> <span class="k">in</span>
|
||
<span class="kd">let</span> <span class="nv">first</span> <span class="p">=</span> <span class="n">firstSecondTuple</span><span class="p">.</span><span class="mi">0</span>
|
||
<span class="kd">let</span> <span class="nv">second</span> <span class="p">=</span> <span class="n">firstSecondTuple</span><span class="p">.</span><span class="mi">1</span>
|
||
<span class="c1">//do something</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">let</span> <span class="nv">stringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
<span class="kd">let</span> <span class="nv">secondFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
|
||
<span class="k">return</span> <span class="n">flatMap</span><span class="p">(</span><span class="n">to</span><span class="p">:</span> <span class="p">(</span><span class="nb">String</span><span class="p">,</span> <span class="nb">String</span><span class="p">,</span> <span class="nb">String</span><span class="p">).</span><span class="kc">self</span><span class="p">,</span> <span class="n">stringFuture</span><span class="p">,</span> <span class="n">secondFuture</span><span class="p">)</span> <span class="p">{</span> <span class="bp">first</span><span class="p">,</span> <span class="n">second</span> <span class="k">in</span>
|
||
<span class="kd">let</span> <span class="nv">thirdFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
<span class="k">return</span> <span class="n">thirdFuture</span><span class="p">.</span><span class="bp">map</span> <span class="p">{</span> <span class="n">third</span> <span class="k">in</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="bp">first</span><span class="p">,</span> <span class="n">second</span><span class="p">,</span> <span class="n">third</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}.</span><span class="bp">map</span> <span class="p">{</span> <span class="bp">first</span><span class="p">,</span> <span class="n">second</span><span class="p">,</span> <span class="n">third</span> <span class="k">in</span>
|
||
<span class="c1">//do something</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<hr/>
|
||
|
||
<p>Always use the global <code>flatMap</code> and <code>map</code> methods to execute futures concurrently when the functions don’t need to wait on each other.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">let</span> <span class="nv">stringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
<span class="kd">let</span> <span class="nv">secondFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
|
||
<span class="k">return</span> <span class="n">stringFuture</span><span class="p">.</span><span class="n">flatMap</span> <span class="p">{</span> <span class="n">string</span> <span class="k">in</span>
|
||
<span class="bp">print</span><span class="p">(</span><span class="n">string</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">secondFuture</span>
|
||
<span class="p">}.</span><span class="bp">map</span> <span class="p">{</span> <span class="n">second</span> <span class="k">in</span>
|
||
<span class="bp">print</span><span class="p">(</span><span class="n">second</span><span class="p">)</span>
|
||
<span class="c1">//finish chain</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">let</span> <span class="nv">stringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
<span class="kd">let</span> <span class="nv">secondFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
|
||
<span class="k">return</span> <span class="n">flatMap</span><span class="p">(</span><span class="n">to</span><span class="p">:</span> <span class="nb">Void</span><span class="p">.</span><span class="kc">self</span><span class="p">,</span> <span class="n">stringFuture</span><span class="p">,</span> <span class="n">secondFuture</span><span class="p">)</span> <span class="p">{</span> <span class="bp">first</span><span class="p">,</span> <span class="n">second</span> <span class="k">in</span>
|
||
<span class="bp">print</span><span class="p">(</span><span class="bp">first</span><span class="p">)</span>
|
||
<span class="bp">print</span><span class="p">(</span><span class="n">second</span><span class="p">)</span>
|
||
|
||
<span class="k">return</span> <span class="p">.</span><span class="n">done</span><span class="p">(</span><span class="n">on</span><span class="p">:</span> <span class="n">req</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<hr/>
|
||
|
||
<p>Avoid nesting async functions more than once per chain, as it becomes unreadable and unsustainable.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">let</span> <span class="nv">stringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
|
||
<span class="k">return</span> <span class="n">stringFuture</span><span class="p">.</span><span class="n">flatMap</span> <span class="p">{</span> <span class="bp">first</span> <span class="k">in</span>
|
||
<span class="kd">let</span> <span class="nv">secondStringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
|
||
<span class="k">return</span> <span class="n">secondStringFuture</span><span class="p">.</span><span class="n">flatMap</span> <span class="p">{</span> <span class="n">second</span> <span class="k">in</span>
|
||
<span class="kd">let</span> <span class="nv">thirdStringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
|
||
<span class="k">return</span> <span class="n">thirdStringFuture</span><span class="p">.</span><span class="n">flatMap</span> <span class="p">{</span> <span class="n">third</span> <span class="k">in</span>
|
||
<span class="bp">print</span><span class="p">(</span><span class="bp">first</span><span class="p">)</span>
|
||
<span class="bp">print</span><span class="p">(</span><span class="n">second</span><span class="p">)</span>
|
||
<span class="bp">print</span><span class="p">(</span><span class="n">third</span><span class="p">)</span>
|
||
|
||
<span class="k">return</span> <span class="p">.</span><span class="n">done</span><span class="p">(</span><span class="n">on</span><span class="p">:</span> <span class="n">req</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">let</span> <span class="nv">stringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
|
||
<span class="k">return</span> <span class="n">stringFuture</span><span class="p">.</span><span class="n">flatMap</span><span class="p">(</span><span class="n">to</span><span class="p">:</span> <span class="p">(</span><span class="nb">String</span><span class="p">,</span> <span class="nb">String</span><span class="p">).</span><span class="kc">self</span><span class="p">)</span> <span class="p">{</span> <span class="bp">first</span> <span class="k">in</span>
|
||
<span class="kd">let</span> <span class="nv">secondStringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
<span class="k">return</span> <span class="n">secondStringFuture</span><span class="p">.</span><span class="n">and</span><span class="p">(</span><span class="n">result</span><span class="p">:</span> <span class="bp">first</span><span class="p">)</span>
|
||
<span class="p">}.</span><span class="n">flatMap</span> <span class="p">{</span> <span class="n">second</span><span class="p">,</span> <span class="bp">first</span> <span class="k">in</span>
|
||
<span class="kd">let</span> <span class="nv">thirdStringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
|
||
<span class="c1">//it's ok to nest once</span>
|
||
<span class="k">return</span> <span class="n">thirdStringFuture</span><span class="p">.</span><span class="n">flatMap</span> <span class="p">{</span> <span class="n">third</span> <span class="k">in</span>
|
||
<span class="bp">print</span><span class="p">(</span><span class="bp">first</span><span class="p">)</span>
|
||
<span class="bp">print</span><span class="p">(</span><span class="n">second</span><span class="p">)</span>
|
||
<span class="bp">print</span><span class="p">(</span><span class="n">third</span><span class="p">)</span>
|
||
|
||
<span class="k">return</span> <span class="p">.</span><span class="n">done</span><span class="p">(</span><span class="n">on</span><span class="p">:</span> <span class="n">req</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<hr/>
|
||
|
||
<p>Use <code>transform(to: )</code> to avoid chaining an extra, unnecessary level.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">let</span> <span class="nv">stringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
|
||
<span class="k">return</span> <span class="n">stringFuture</span><span class="p">.</span><span class="bp">map</span> <span class="p">{</span> <span class="kc">_</span> <span class="k">in</span>
|
||
<span class="k">return</span> <span class="p">.</span><span class="n">ok</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">let</span> <span class="nv">stringFuture</span><span class="p">:</span> <span class="n">Future</span><span class="p"><</span><span class="nb">String</span><span class="p">></span>
|
||
<span class="k">return</span> <span class="n">stringFuture</span><span class="p">.</span><span class="n">transform</span><span class="p">(</span><span class="n">to</span><span class="p">:</span> <span class="p">.</span><span class="n">ok</span><span class="p">)</span>
|
||
</pre></div>
|
||
|
||
|
||
<hr/>
|
||
|
||
<p>Avoid synchronous throwing where possible as it can lead to undefined behavior in the route, specifically because running IO operations may not finish before the route ends.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">func</span> <span class="nf">index</span><span class="p">(</span><span class="kc">_</span> <span class="n">req</span><span class="p">:</span> <span class="n">Request</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">-></span> <span class="n">Future</span><span class="p"><</span><span class="n">View</span><span class="p">></span> <span class="p">{</span>
|
||
<span class="kd">let</span> <span class="nv">models</span> <span class="p">=</span> <span class="n">SomeModel</span><span class="p">.</span><span class="n">query</span><span class="p">(</span><span class="n">on</span><span class="p">:</span> <span class="n">req</span><span class="p">).</span><span class="n">all</span><span class="p">()</span>
|
||
|
||
<span class="k">guard</span> <span class="n">someCondition</span> <span class="k">else</span> <span class="p">{</span> <span class="k">throw</span> <span class="n">SomeError</span><span class="p">(...)</span> <span class="p">}</span>
|
||
|
||
<span class="k">return</span> <span class="n">req</span><span class="p">.</span><span class="n">view</span><span class="p">().</span><span class="n">render</span><span class="p">(</span><span class="s">"someView"</span><span class="p">,</span> <span class="p">[</span><span class="s">"models"</span><span class="p">:</span> <span class="n">models</span><span class="p">])</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">func</span> <span class="nf">index</span><span class="p">(</span><span class="kc">_</span> <span class="n">req</span><span class="p">:</span> <span class="n">Request</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">-></span> <span class="n">Future</span><span class="p"><</span><span class="n">View</span><span class="p">></span> <span class="p">{</span>
|
||
<span class="kd">let</span> <span class="nv">modelsFuture</span> <span class="p">=</span> <span class="n">SomeModel</span><span class="p">.</span><span class="n">query</span><span class="p">(</span><span class="n">on</span><span class="p">:</span> <span class="n">req</span><span class="p">).</span><span class="n">all</span><span class="p">()</span>
|
||
<span class="k">return</span> <span class="n">modelsFuture</span><span class="p">.</span><span class="n">flatMap</span> <span class="p">{</span> <span class="n">models</span> <span class="k">in</span>
|
||
<span class="k">guard</span> <span class="n">someCondition</span> <span class="k">else</span> <span class="p">{</span> <span class="k">throw</span> <span class="n">SomeError</span><span class="p">(...)</span> <span class="p">}</span>
|
||
<span class="k">return</span> <span class="n">req</span><span class="p">.</span><span class="n">view</span><span class="p">().</span><span class="n">render</span><span class="p">(</span><span class="s">"someView"</span><span class="p">,</span> <span class="p">[</span><span class="s">"models"</span><span class="p">:</span> <span class="n">models</span><span class="p">])</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<h2 id="testing">Testing<a class="headerlink" href="#testing" title="Permanent link">¶</a></h2>
|
||
<p>Testing is a crucial part of Vapor applications that helps ensure feature parity across versions. We strongly recommend testing for all Vapor applications.</p>
|
||
<p>While testing routes, avoid changing behavior only to accommodate for the testing environment. Instead, if there is functionality that should differ based on the environment, you should create a service and swap out the selected version during the testing configuration.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">func</span> <span class="nf">login</span><span class="p">(</span><span class="n">req</span><span class="p">:</span> <span class="n">Request</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">-></span> <span class="n">Future</span><span class="p"><</span><span class="n">View</span><span class="p">></span> <span class="p">{</span>
|
||
<span class="k">if</span> <span class="n">req</span><span class="p">.</span><span class="n">environment</span> <span class="o">!=</span> <span class="p">.</span><span class="n">testing</span> <span class="p">{</span>
|
||
<span class="k">try</span> <span class="n">req</span><span class="p">.</span><span class="n">verifyCSRF</span><span class="p">()</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="c1">//rest of the route</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">func</span> <span class="nf">login</span><span class="p">(</span><span class="n">req</span><span class="p">:</span> <span class="n">Request</span><span class="p">)</span> <span class="kr">throws</span> <span class="p">-></span> <span class="n">Future</span><span class="p"><</span><span class="n">View</span><span class="p">></span> <span class="p">{</span>
|
||
<span class="kd">let</span> <span class="nv">csrf</span> <span class="p">=</span> <span class="k">try</span> <span class="n">req</span><span class="p">.</span><span class="n">make</span><span class="p">(</span><span class="n">CSRF</span><span class="p">.</span><span class="kc">self</span><span class="p">)</span>
|
||
<span class="k">try</span> <span class="n">csrf</span><span class="p">.</span><span class="n">verify</span><span class="p">(</span><span class="n">req</span><span class="p">:</span> <span class="n">req</span><span class="p">)</span>
|
||
<span class="c1">//rest of the route</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Note how the correct way of handling this situation includes making a service - this is so that you can mock out fake functionality in the testing version of the service.</p>
|
||
<p>Every test should setup and teardown your database. <strong>Do not</strong> try and persist state between tests.</p>
|
||
<p>Tests should be separated into unit tests and integration. If using the repository pattern, the unit tests should use the memory version of the repositories while the integration tests should use the database version of the repositories.</p>
|
||
<h2 id="fluent">Fluent<a class="headerlink" href="#fluent" title="Permanent link">¶</a></h2>
|
||
<p>ORMs are notorious for making it really easy to write bad code that works but is terribly inefficient or incorrect. Fluent tends to minimize this possibility thanks to the usage of features like KeyPaths and strongly-typed decoding, but there are still a few things to watch out for.</p>
|
||
<p>Actively watch out for and avoid code that produces N+1 queries. Queries that have to be run for every instance of a model are bad and typically produce N+1 problems. Another identifying feature of N+1 code is the combination of a loop (or <code>map</code>) with <code>flatten</code>.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="c1">//assume this is filled and that each owner can have one pet</span>
|
||
<span class="kd">let</span> <span class="nv">owners</span> <span class="p">=</span> <span class="p">[</span><span class="n">Owner</span><span class="p">]()</span>
|
||
<span class="kd">var</span> <span class="nv">petFutures</span> <span class="p">=</span> <span class="p">[</span><span class="n">Future</span><span class="p"><</span><span class="n">Pet</span><span class="p">>]()</span>
|
||
|
||
<span class="k">for</span> <span class="n">owner</span> <span class="k">in</span> <span class="n">owners</span> <span class="p">{</span>
|
||
<span class="kd">let</span> <span class="nv">petFuture</span> <span class="p">=</span> <span class="k">try</span> <span class="n">Pet</span><span class="p">.</span><span class="bp">find</span><span class="p">(</span><span class="n">owner</span><span class="p">.</span><span class="n">petId</span><span class="p">,</span> <span class="n">on</span><span class="p">:</span> <span class="n">req</span><span class="p">).</span><span class="n">unwrap</span><span class="p">(</span><span class="n">or</span><span class="p">:</span> <span class="n">Abort</span><span class="p">(.</span><span class="n">badRequest</span><span class="p">))</span>
|
||
<span class="n">petFutures</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">petFuture</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">let</span> <span class="nv">allPets</span> <span class="p">=</span> <span class="n">petFutures</span><span class="p">.</span><span class="n">flatten</span><span class="p">(</span><span class="n">on</span><span class="p">:</span> <span class="n">req</span><span class="p">)</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="c1">//assume this is filled and that each owner can have one pet</span>
|
||
<span class="kd">let</span> <span class="nv">owners</span> <span class="p">=</span> <span class="p">[</span><span class="n">Owner</span><span class="p">]()</span>
|
||
<span class="kd">let</span> <span class="nv">petIds</span> <span class="p">=</span> <span class="n">owners</span><span class="p">.</span><span class="n">compactMap</span> <span class="p">{</span> <span class="nv">$0</span><span class="p">.</span><span class="n">petId</span> <span class="p">}</span>
|
||
<span class="kd">let</span> <span class="nv">allPets</span> <span class="p">=</span> <span class="k">try</span> <span class="n">Pet</span><span class="p">.</span><span class="n">query</span><span class="p">(</span><span class="n">on</span><span class="p">:</span> <span class="n">req</span><span class="p">).</span><span class="bp">filter</span><span class="p">(</span><span class="err">\</span><span class="p">.</span><span class="n">id</span> <span class="o">~~</span> <span class="n">petIds</span><span class="p">).</span><span class="n">all</span><span class="p">()</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Notice the use of the <code>~~</code> infix operator which creates an <code>IN</code> SQL query.</p>
|
||
<hr/>
|
||
|
||
<p>In addition to reducing Fluent inefficiencies, opt for using native Fluent queries over raw queries unless your intended query is too complex to be created using Fluent.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="n">conn</span><span class="p">.</span><span class="n">raw</span><span class="p">(</span><span class="s">"SELECT * FROM users;"</span><span class="p">)</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="n">User</span><span class="p">.</span><span class="n">query</span><span class="p">(</span><span class="n">on</span><span class="p">:</span> <span class="n">req</span><span class="p">).</span><span class="n">all</span><span class="p">()</span>
|
||
</pre></div>
|
||
|
||
|
||
<h2 id="leaf">Leaf<a class="headerlink" href="#leaf" title="Permanent link">¶</a></h2>
|
||
<p>Creating clean, readable Leaf files is important. One of the ways to go about doing this is through the use of base templates. Base templates allow you to specify only the different part of the page in the main leaf file for that view, and then base template will sub in the common components of the page (meta headers, the page footer, etc). For example:</p>
|
||
<p><code>base.leaf</code></p>
|
||
<div class="codehilite"><pre><span></span><span class="cp"><!DOCTYPE html></span> <span class="c"><!-- HTML5 --></span>
|
||
<span class="p"><</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">"en"</span><span class="p">></span>
|
||
<span class="p"><</span><span class="nt">head</span><span class="p">></span>
|
||
<span class="c"><!-- Basic Meta --></span>
|
||
<span class="p"><</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">"utf-8"</span><span class="p">></span>
|
||
<span class="p"><</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">"viewport"</span> <span class="na">content</span><span class="o">=</span><span class="s">"width=device-width, initial-scale=1"</span><span class="p">></span>
|
||
<span class="p"><</span><span class="nt">meta</span> <span class="na">http-equiv</span><span class="o">=</span><span class="s">"x-ua-compatible"</span> <span class="na">content</span><span class="o">=</span><span class="s">"ie=edge"</span><span class="p">></span>
|
||
|
||
<span class="p"><</span><span class="nt">title</span><span class="p">></span>#get(title)<span class="p"></</span><span class="nt">title</span><span class="p">></span>
|
||
<span class="p"></</span><span class="nt">head</span><span class="p">></span>
|
||
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
|
||
#get(body)
|
||
#embed("Views/footer")
|
||
<span class="p"></</span><span class="nt">body</span><span class="p">></span>
|
||
<span class="p"></</span><span class="nt">html</span><span class="p">></span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Notice the calls to <code>#get</code> and <code>#embed</code> which piece together the supplied variables from the view and create the final HTML page.</p>
|
||
<p><code>login.leaf</code></p>
|
||
<div class="codehilite"><pre><span></span>#set("title") { Login }
|
||
|
||
#set("body") {
|
||
<span class="p"><</span><span class="nt">p</span><span class="p">></span>Add your login page here<span class="p"></</span><span class="nt">p</span><span class="p">></span>
|
||
}
|
||
|
||
#embed("Views/base")
|
||
</pre></div>
|
||
|
||
|
||
<p>In addition to extracting base components to one file, you should also extract common components to their own file. For example, instead of repeating the snippet to create a bar graph, put it inside of a different file and then use <code>#embed()</code> to pull it into your main view.</p>
|
||
<p>Always use <code>req.view()</code> to render the views for your frontend. This will ensure that the views will take advantage of caching in production mode, which dramatically speeds up your frontend responses.</p>
|
||
<h2 id="errors">Errors<a class="headerlink" href="#errors" title="Permanent link">¶</a></h2>
|
||
<p>Depending on the type of application you are building (frontend, API-based, or hybrid) the way that you throw and handle errors may differ. For example, in an API-based system, throwing an error generally means you want to return it as a response. However, in a frontend system, throwing an error most likely means that you will want to handle it further down the line to give the user contextual frontend information.</p>
|
||
<p>As a general rule of thumb, conform all of your custom error types to Debuggable. That helps <code>ErrorMiddleware</code> print better diagnostics and can lead to easier debugging.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">enum</span> <span class="nc">CustomError</span><span class="p">:</span> <span class="n">Error</span> <span class="p">{</span>
|
||
<span class="k">case</span> <span class="n">error</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">enum</span> <span class="nc">CustomError</span><span class="p">:</span> <span class="n">Debuggable</span> <span class="p">{</span>
|
||
<span class="k">case</span> <span class="n">error</span>
|
||
|
||
<span class="c1">//</span><span class="cs">MARK:</span><span class="c1"> - Debuggable</span>
|
||
<span class="kd">var</span> <span class="nv">identifier</span><span class="p">:</span> <span class="nb">String</span> <span class="p">{</span>
|
||
<span class="k">switch</span> <span class="kc">self</span> <span class="p">{</span>
|
||
<span class="k">case</span> <span class="p">.</span><span class="n">error</span><span class="p">:</span> <span class="k">return</span> <span class="s">"error"</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">var</span> <span class="nv">reason</span><span class="p">:</span> <span class="nb">String</span> <span class="p">{</span>
|
||
<span class="k">switch</span> <span class="kc">self</span> <span class="p">{</span>
|
||
<span class="k">case</span> <span class="p">.</span><span class="n">error</span><span class="p">:</span> <span class="k">return</span> <span class="s">"Specify reason here"</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
|
||
|
||
<hr/>
|
||
|
||
<p>Include a <code>reason</code> when throwing generic <code>Abort</code> errors to indicate the context of the situation.</p>
|
||
<p><strong>Bad:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="k">throw</span> <span class="n">Abort</span><span class="p">(.</span><span class="n">badRequest</span><span class="p">)</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><strong>Good:</strong></p>
|
||
<div class="codehilite"><pre><span></span><span class="k">throw</span> <span class="n">Abort</span><span class="p">(.</span><span class="n">badRequest</span><span class="p">,</span> <span class="n">reason</span><span class="p">:</span> <span class="s">"Could not get data from external API."</span><span class="p">)</span>
|
||
</pre></div>
|
||
|
||
|
||
<h2 id="3rd-party-providers">3rd Party Providers<a class="headerlink" href="#3rd-party-providers" title="Permanent link">¶</a></h2>
|
||
<p>When building third party providers for Vapor, it's important to have a certain consistency that users will be able to become familiar with when switching or adding new providers. Although Vapor is very young, there are already certain patterns that make sense when writing providers.</p>
|
||
<p>When creating a provider library, you should omit phrases like <code>Provider</code> or <code>Package</code>. Take the StripeProvider for example, while the name of the project itself can be named <code>StripeProvider</code> the library name should be just the product itself:</p>
|
||
<div class="codehilite"><pre><span></span><span class="kd">let</span> <span class="nv">package</span> <span class="p">=</span> <span class="n">Package</span><span class="p">(</span>
|
||
<span class="n">name</span><span class="p">:</span> <span class="s">"StripeProvider"</span><span class="p">,</span>
|
||
<span class="n">products</span><span class="p">:</span> <span class="p">[</span>
|
||
<span class="p">.</span><span class="n">library</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="s">"Stripe"</span><span class="p">,</span> <span class="n">targets</span><span class="p">:</span> <span class="p">[</span><span class="s">"Stripe"</span><span class="p">])</span>
|
||
<span class="p">],</span>
|
||
<span class="p">)</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>This allows for easy to read and clean import statements:
|
||
<code>import Stripe</code> rather than <code>import StripeProvider</code>.</p>
|
||
<h2 id="overall-advice">Overall Advice<a class="headerlink" href="#overall-advice" title="Permanent link">¶</a></h2>
|
||
<ul>
|
||
<li>Use <code>//MARK:</code> to denote sections of your controllers or configuration so that it is easier for other project members to find critically important areas.</li>
|
||
<li>Only import modules that are needed for that specific file. Adding extra modules creates bloat and makes it difficult to deduce that controller’s responsibility.</li>
|
||
<li>Where possible, use Swift doc-blocks to document methods. This is especially important for methods implements on entities so that other project members understand how the function affects persisted data.</li>
|
||
<li>Do not retrieve environment variables on a repeated basis. Instead, use a custom service and register those variables during the configuration stage of your application (see “Configuration”)</li>
|
||
<li>Reuse <code>DateFormatters</code> where possible (while also maintaining thread safety). In particular, don’t create a date formatter inside of a loop as they are expensive to make.</li>
|
||
<li>Store dates in a computer-readable format until the last possible moment when they must be converted to human-readable strings. That conversion is typically very expensive and is unnecessary when passing dates around internally. Offloading this responsibility to JavaScript is a great tactic as well if you are building a front-end application.</li>
|
||
<li>Eliminate stringly-typed code where possible by storing frequently used strings in a file like <code>Constants.swift</code>.</li>
|
||
</ul>
|
||
<h2 id="maintainers">Maintainers<a class="headerlink" href="#maintainers" title="Permanent link">¶</a></h2>
|
||
<p>This style guide was written and is maintained by the following Vapor members:</p>
|
||
<ul>
|
||
<li>Andrew (<a href="https://github.com/andrewangeta">@andrewangeta</a>)</li>
|
||
<li>Jimmy (<a href="https://github.com/mcdappdev">@mcdappdev</a>) (Project manager)</li>
|
||
<li>Jonas (<a href="https://github.com/joscdk">@joscdk</a>)</li>
|
||
<li>Tanner (<a href="https://github.com/tanner0101">@tanner0101</a>)</li>
|
||
<li>Tim (<a href="https://github.com/0xtim">@0xtim</a>)</li>
|
||
<li>Gustavo (<a href="https://github.com/gperdomor">@gperdomor</a>)</li>
|
||
</ul>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<h2 id="__comments">Comments</h2>
|
||
<div id="disqus_thread"></div>
|
||
<script>
|
||
var disqus_config = function () {
|
||
this.page.url = "https://docs.vapor.codes/3.0/extras/style-guide/";
|
||
this.page.identifier =
|
||
"extras/style-guide/";
|
||
};
|
||
(function() {
|
||
var d = document, s = d.createElement("script");
|
||
s.src = "//vapor-docs.disqus.com/embed.js";
|
||
s.setAttribute("data-timestamp", +new Date());
|
||
(d.head || d.body).appendChild(s);
|
||
})();
|
||
</script>
|
||
|
||
|
||
</article>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
|
||
<footer class="md-footer">
|
||
|
||
<div class="md-footer-nav">
|
||
<nav class="md-footer-nav__inner md-grid">
|
||
|
||
<a href="../../websocket/overview/" title="Overview" class="md-flex md-footer-nav__link md-footer-nav__link--prev" rel="prev">
|
||
<div class="md-flex__cell md-flex__cell--shrink">
|
||
<i class="md-icon md-icon--arrow-back md-footer-nav__button"></i>
|
||
</div>
|
||
<div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title">
|
||
<span class="md-flex__ellipsis">
|
||
<span class="md-footer-nav__direction">
|
||
Previous
|
||
</span>
|
||
Overview
|
||
</span>
|
||
</div>
|
||
</a>
|
||
|
||
|
||
<a href="../yeoman/" title="Yeoman" class="md-flex md-footer-nav__link md-footer-nav__link--next" rel="next">
|
||
<div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title">
|
||
<span class="md-flex__ellipsis">
|
||
<span class="md-footer-nav__direction">
|
||
Next
|
||
</span>
|
||
Yeoman
|
||
</span>
|
||
</div>
|
||
<div class="md-flex__cell md-flex__cell--shrink">
|
||
<i class="md-icon md-icon--arrow-forward md-footer-nav__button"></i>
|
||
</div>
|
||
</a>
|
||
|
||
</nav>
|
||
</div>
|
||
|
||
<div class="md-footer-meta md-typeset">
|
||
<div class="md-footer-meta__inner md-grid">
|
||
<div class="md-footer-copyright">
|
||
|
||
<div class="md-footer-copyright__highlight">
|
||
Copyright © 2018 Qutheory, LLC
|
||
</div>
|
||
|
||
powered by
|
||
<a href="https://www.mkdocs.org">MkDocs</a>
|
||
and
|
||
<a href="https://squidfunk.github.io/mkdocs-material/">
|
||
Material for MkDocs</a>
|
||
</div>
|
||
|
||
|
||
<div class="md-footer-social">
|
||
<link rel="stylesheet" href="../../assets/fonts/font-awesome.css">
|
||
|
||
<a href="https://twitter.com/@codevapor" class="md-footer-social__link fa fa-twitter"></a>
|
||
|
||
<a href="http://vapor.team/" class="md-footer-social__link fa fa-slack"></a>
|
||
|
||
<a href="https://github.com/vapor" class="md-footer-social__link fa fa-github"></a>
|
||
|
||
</div>
|
||
|
||
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
|
||
</div>
|
||
|
||
<script src="../../assets/javascripts/application.5e60981f.js"></script>
|
||
|
||
<script>app.initialize({version:"1.0.4",url:{base:"../.."}})</script>
|
||
|
||
|
||
|
||
|
||
<script>!function(e,a,t,n,o,c,i){e.GoogleAnalyticsObject=o,e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,c=a.createElement(t),i=a.getElementsByTagName(t)[0],c.async=1,c.src="https://www.google-analytics.com/analytics.js",i.parentNode.insertBefore(c,i)}(window,document,"script",0,"ga"),ga("create","UA-76177358-4","auto"),ga("set","anonymizeIp",!0),ga("send","pageview");var links=document.getElementsByTagName("a");if(Array.prototype.map.call(links,function(e){e.host!=document.location.host&&e.addEventListener("click",function(){var a=e.getAttribute("data-md-action")||"follow";ga("send","event","outbound",a,e.href)})}),document.forms.search){var query=document.forms.search.query;query.addEventListener("blur",function(){if(this.value){var e=document.location.pathname;ga("send","pageview",e+"?q="+this.value)}})}</script>
|
||
|
||
|
||
</body>
|
||
</html> |