+ + + + + + Using Client¶ +Client is a convenience wrapper around the lower level HTTP → Client. It automatically parses things like hostname and port from URIs and helps you encode and decode Content. +let res = try req.client().get("http://vapor.codes") +print(res) // Future<Response> + + + +Container¶ +The first thing you will need is a service Container to create your client. +If you are making this external API request as the result of an incoming request to your server, you should use the Request container to create a client. This is most often the case. +If you need a client during boot, use the Application or if you are in a Command use the command context's container. +Once you have a Container, use the client() method to create a Client. +// Creates a generic Client +let client = try container.client() + + + +Send¶ +Once you have a Client, you can use the send(...) method to send a Request. Note that the request URI must include a scheme and hostname. +let req: Request ... +let res = try client.send(req) +print(res) // Future<Response> + + + +You can also use the convenience methods like get(...), post(...), etc. +let user: User ... +let res = try client.post("http://api.vapor.codes/users") { post in + try post.content.encode(user) +} +print(res) // Future<Response> + + + +See Content for more information on encoding and decoding content to messages. + + + + + + + +
+ + + + + + Using Sessions¶ +This guide will show you how to use sessions in Vapor to maintain state for a connected client. +Sessions work by creating unique identifiers for each new client and asking the client to supply this identifier with each request. When the next request is received, Vapor uses this unique identifier to restore the session data. This identifier could be transmitted in any format, but it is almost always done with cookies and that is how Vapor's sessions work. +When a new client connects and session data is set, Vapor will return a Set-Cookie header. The client is then expected to re-supply the value with each request in a Cookie header. All browsers do this automatically. If your ever decide to invalidate the session, Vapor will delete any related data and notify the client that their cookie is no longer valid. +Middleware¶ +The first step to using sessions is enabling SessionsMiddleware. This can be done globally for the entire application or on a per-route basis. +Globally¶ +To globally enable sessions, add the middleware to your MiddlewareConfig. +var middlewares = MiddlewareConfig.default() +middlewares.use(SessionsMiddleware.self) +services.register(middlewares) + + + +This is usually done in configure.swift. +Per Route¶ +To enable sessions for a group of routes, use the grouped(...) methods on Router. +// create a grouped router at /sessions w/ sessions enabled +let sessions = router.grouped("sessions").grouped(SessionsMiddleware.self) + +// create a route at GET /sessions/foo +sessions.get("foo") { req in + // use sessions +} + + + +Sessions¶ +When SessionsMiddleware boots it will attempt to make a Sessions and a SessionsConfig. Vapor will use an in-memory session by default. You can override both of these services by registering them in configure.swift. +You can use Fluent databases (like MySQL, PostgreSQL, etc) or caches like Redis to store your sessions. See the respective guides for more information. +Session¶ +Once you have SessionsMiddleware enabled, you can use req.session() to access the session. Here is a simple example that does simple CRUD operations on a "name" value in the session. +// create a route at GET /sessions/get +sessions.get("get") { req -> String in + // access "name" from session or return n/a + return try req.session()["name"] ?? "n/a" +} + +// create a route at GET /sessions/set/:name +sessions.get("set", String.parameter) { req -> String in + // get router param + let name = try req.parameters.next(String.self) + + // set name to session at key "name" + try req.session()["name"] = name + + // return the newly set name + return name +} + +// create a route at GET /sessions/del +sessions.get("del") { req -> String in + // destroy the session + try req.destroySession() + + // signal success + return "done" +} + + + +That's it, congratulations on getting sessions working! + + + + + + + +
+ + + + + + Using WebSockets¶ +Vapor includes convenience methods for working with the lower level WebSocket client and server. +Server¶ +Vapor's WebSocket server includes the ability to route incoming requests just like its HTTP server. +When Vapor's main HTTP Server boots it will attempt to create a WebSocketServer. If one is registered, it will be added as an HTTP upgrade handler to the server. +So to create a WebSocket server, all you need to do is register one in configure.swift. +// Create a new NIO websocket server +let wss = NIOWebSocketServer.default() + +// Add WebSocket upgrade support to GET /echo +wss.get("echo") { ws, req in + // Add a new on text callback + ws.onText { ws, text in + // Simply echo any received text + ws.send(text) + } +} + +// Register our server +services.register(wss, as: WebSocketServer.self) + + + +That's it. Next time you boot your server, you will be able to perform a WebSocket upgrade at GET /echo. You can test this using a simple command line tool called wsta available for macOS and Linux. +$ wsta ws://localhost:8080/echo +Connected to ws://localhost:8080/echo +hello, world! +hello, world! + + + +Parameters¶ +Like Vapor's HTTP router, you can also use routing parameters with your WebSocket server. +// Add WebSocket upgrade support to GET /chat/:name +wss.get("chat", String.parameter) { ws, req in + let name = try req.parameters.next(String.self) + ws.send("Welcome, \(name)!") + + // ... +} + + + +Now let's test this new route: +$ wsta ws://localhost:8080/chat/Vapor +Connected to ws://localhost:8080/chat/Vapor +Welcome, Vapor! + + + +Client¶ +Vapor also supports connecting to WebSocket servers as a client. The easiest way to connect to a WebSocket server is through the webSocket(...) method on Client. +For this example, we will assume our application connects to a WebSocket server in boot.swift +// connect to echo.websocket.org +let done = try app.client().webSocket("ws://echo.websocket.org").flatMap { ws -> Future<Void> in + // setup an on text callback that will print the echo + ws.onText { ws, text in + print("rec: \(text)") + // close the websocket connection after we recv the echo + ws.close() + } + + // when the websocket first connects, send message + ws.send("hello, world!") + + // return a future that will complete when the websocket closes + return ws.onClose +} + +print(done) // Future<Void> + +// wait for the websocket to close +try done.wait() + + + + + + + + +
+ + + + + + Upgrading Versions¶ +This document provides information about changes between version and tips for migrating your projects. +2.4 to 3.0¶ +Vapor 3 has been rewritten from the ground up to be async and event-driven. This release contains the most changes of any previous release (and most likely any future release). +Because of this, it is recommended that to migrate your projects you start by creating a new, empty template and migrate by copy / pasting code over to the new project. +We recommend reading the Getting Started → Hello, world! section for Vapor 3 to familiarize yourself with the new APIs. +Async¶ +The biggest change in Vapor 3 is that the framework is now completely asynchronous. When you call methods that need to perform slow work like network requests or disk access instead of blocking they will now return a Future<T>. +Futures are values that may not exist yet, so you cannot interact with them directly. Instead, you must use map/flatMap to access the values. +// vapor 2 +let res = try drop.client.get("http://vapor.codes") +print(res.status) // HTTPStatus +return res.status + +// vapor 3 +let f = try req.client().get("http://vapor.codes").map { res in + print(res.http.status) // HTTPStatus + return res.http.status +} +print(f) // Future<HTTPStatus> + + + +See Async → Getting Started to learn more. +Application & Services¶ +Droplet has been renamed to Application and is now a service-container. In Vapor 2, the Droplet had stored properties for things you would need during development (like views, hashers, etc). In Vapor 3, this is all done via services. +While the Application is a service-container, you should not use it from your route closures. This is to prevent race conditions since Vapor runs on multiple threads (event loops). Instead, use the Request that is supplied to your route closure. This has a copy of all of the application's services for you to use. +// vapor 2 +return try drop.view.make("myView") + +// vapor 3 +return try req.make(ViewRenderer.self).render("myView") +// shorthand +return try req.view().render("myView") + + + +See Service → Getting Started to learn more. +Database Connections¶ +In Vapor 3, database connections are no longer statically accessible. This makes doing things like transactions and connection pooling much more predictable and performant. +In order to create a QueryBuilder in Fluent 3, you will need access to something DatabaseConnectable. Most often you can just use the incoming Request, but you can also create connections manually if you need. +// vapor 2 +User.makeQuery().all() + +// vapor 3 +User.query(on: req).all() + + + +See DatabaseKit → Getting Started to learn more. +Work in progress¶ +This migration guide is a work in progress. Please feel free to add any migration tips here by submitting a PR. +Join the #upgrading-to-3 in Vapor's team chat to ask questions and get help in real time. + + + + + + + +