Skip to content

Commit ea0dedc

Browse files
committed
📚 Update README.md
1 parent 115f02e commit ea0dedc

File tree

1 file changed

+169
-3
lines changed

1 file changed

+169
-3
lines changed

README.md

Lines changed: 169 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,177 @@
11
# Vapor Route Builder
22

3-
Build Vapor routes using a declarative syntax langauge similar to how you would write SwiftUI views.
3+
Build and define Vapor routes using a declarative syntax langauge similar to how you would write SwiftUI views.
4+
5+
[Learn more](https://github.com/vapor/vapor) about 💧 Vapor, an HTTP web framework for Swift.
6+
7+
## Usage
8+
9+
> [!IMPORTANT]
10+
> Before learning about `@RouteBuilder`, make sure you've [learned the basics](https://docs.vapor.codes/basics/routing/) of routing with Vapor.
11+
12+
Defining routes using `@RouteBuilder` is fairly straightforward. You simply call `.register(_:)` on your `Application`, providing the routes that you wish to support.
13+
14+
```swift
15+
app.register {
16+
GET("latest") { ... }
17+
GET("popular") { ... }
18+
POST("favorite") { ... }
19+
}
20+
```
21+
22+
### Path Groups
23+
24+
You can use `Group` to help organize your routes and cut down on the repetitiveness of defining long paths.
25+
26+
```swift
27+
app.register {
28+
Group(path: "api", "v1") {
29+
Group(path: "movies") {
30+
GET("latest") { ... }
31+
GET("popular") { ... }
32+
GET(":movie") { ... }
33+
}
34+
Group(path: "books") {
35+
GET("new") { ... }
36+
GET("trending") { ... }
37+
GET(":book") { ... }
38+
}
39+
}
40+
}
41+
```
42+
43+
### Middleware Groups
44+
45+
Sometimes you may want to wrap certain routes in middleware. A common use case requiring middleware could be authentication. You have have some routes that require middleware, and others that do now. Adding middleware using `@RouteBuilder` is similar to wrapping routes in `Group(path:)`.
46+
47+
```swift
48+
app.register {
49+
Group(path: "api", "v1") {
50+
Group(middleware: AuthenticationMiddleware()) {
51+
Group(path: "profile") {
52+
GET("favorites") { ... }
53+
GET("friends") { ... }
54+
}
55+
}
56+
Group(path: "books") {
57+
GET("new") { ... }
58+
GET("trending") { ... }
59+
GET(":book") { ... }
60+
}
61+
}
62+
}
63+
```
64+
65+
In the above example, you'll see that we've only wrapped our `/profile/*` endpoints in our `AuthenticationMiddleware`, while all of the `/book/*` endpoints have no middleware associated with them.
66+
67+
If you have more than one middleware... no worries, `Group(middleware:)` accepts a variadic amount of middleware.
68+
69+
```swift
70+
Group(middleware: Logging()) {
71+
GET("foo") { ... }
72+
Group(middleware: Authentication(), Validator()) {
73+
GET("bar") { ... }
74+
}
75+
}
76+
```
77+
78+
Remember that order matters here. Incoming requests will always execute middleware from top to bottom. So in the above example, the order of an incoming request would be as follows ➡️ `Logging`, `Authentication`, `Validator`. Outgoing respones will always execute middleware in the reverse order. ➡️ `Validator`, `Authentication`, `Logging`.
79+
80+
### Making Custom Route Components
81+
82+
Often times, as your routes grow, a single large definition can become unwieldly and cumbersome to read and update. Organization of routes can be straightforward with `@RouteBuilder`
83+
84+
```swift
85+
struct MoviesUserCase: RouteComponent {
86+
var body: some RouteComponent {
87+
Group(path: "movies") {
88+
MovieUseCase()
89+
GET("latest") { ... }
90+
GET("trending") { ... }
91+
}
92+
}
93+
}
94+
95+
struct MovieUseCase: RouteComponent {
96+
var body: some RouteComponent {
97+
Group(path: ":movie") {
98+
GET("credits") { ... }
99+
GET("related") { ... }
100+
}
101+
}
102+
}
103+
104+
struct BooksUseCase: RouteComponent {
105+
var body: some RouteComponent {
106+
...
107+
}
108+
}
109+
110+
app.register {
111+
MoviesUseCase()
112+
BooksUseCase()
113+
}
114+
```
115+
116+
### Convenience Routes
117+
118+
This package ships with a few conveniences for creating routes. You can use `GET`, `POST`, `PUT`, `PATCH`, and `DELETE` to cut down on the verbosity of defining your routes. If you need more fine grained control, you can always fall back to using a `Route` directly in your `@RouteBuilder`
119+
120+
```swift
121+
Group(path: "movies", ":movie") {
122+
Route(.OPTIONS, "credits") { ... }
123+
}
124+
```
125+
126+
### Expressions & Logic
127+
128+
`@ResultBuilder` supports a wide variety of the available result builder syntax.
129+
130+
```swift
131+
app.register {
132+
if isStaging {
133+
GET("configuration") { ... }
134+
POST("configuration") { ... }
135+
}
136+
137+
GET("latest") { ... }
138+
}
139+
```
140+
141+
```swift
142+
app.register {
143+
for category in categories {
144+
Group(path: "\(category.rawValue)") {
145+
switch category {
146+
case .movies:
147+
GET(":movie") { ... }
148+
case .books:
149+
GET(":book") { ... }
150+
}
151+
}
152+
}
153+
}
154+
```
155+
156+
### RouteModifiers
157+
158+
- Currently undocumented.
4159

5160
## TODO
6161

7-
- [Confirmed] Add README usage docs.
8162
- [Confirmed] Implement Websocket support
163+
- [Confirmed] Handle routes at the root of a RouteComponent.
164+
165+
```swift
166+
Group(path: ":movie") {
167+
... How do we handle JUST ":movie"?
168+
GET("credits") { ... }
169+
GET("category") { ... }
170+
}
171+
```
172+
9173
- [Maybe] Implement EnvironmentObject style support.
10174
- [Maybe] Implement Service support
11-
175+
- [Maybe] Route modifier for description.
176+
- [Maybe] Route modifier for caseInsensitive.
177+
- [Maybe] Route modifier for defaultMaxBodySize.

0 commit comments

Comments
 (0)