This package is meant to help you using Inertia JS in your Vapor 4 project.
- Render React/Vue/Svelte from controllers
- Flash data passed to props
- Assets Versioning: https://inertiajs.com/asset-versioning
- Lazy Evaluation: https://inertiajs.com/responses#lazy-evaluation
- Auto put response cookie for crsf token: https://inertiajs.com/security#csrf-protection
- Override redirect codes: https://inertiajs.com/redirects#303-response-code
- Partial reloads: https://inertiajs.com/requests#partial-reloads
- Shared data interface: https://inertiajs.com/shared-data
Add the adapter in your dependencies array in Package.swift:
dependencies: [
// ...,
.package(name: "vapor-inertia-adapter",
url: "https://github.com/lloople/vapor-inertia-adapter.git",
from: "0.3.0")
],
Also ensure you add it as a dependency to your target:
targets: [
.target(name: "App", dependencies: [
.product(name: "Vapor", package: "vapor"),
// ...,
.product(name:"VaporInertiaAdapter", package:"vapor-inertia-adapter")
]),
// ...
]
This package provides a middleware that checks the responses before returning them to the client for further adjustements. You should apply this middleware to all your application routes in configure.swift:
app.middleware.use(InertiaMiddleware())
Inertia will reload the whole page when it detects the assets changed. This is handled by a version token sent from the server. When the client detects that the previous token it has is different from the one returned from the server, it knows it's time to download the assets again.
This version token should be configured in configure.swift:
Inertia.instance().setVersion("v1.0.1")
Ideally, you should use some kind of hash generated via your assets files, or change it manually in each update of your CSS or JS files. Laravel Mix is a good choice. A sample helper was provided in vapor.mix.js and webpack.mix.js.
Inertia.js Docs: https://inertiajs.com/shared-data
To share data:
// could also be request.instance
Inertia.instance().share("hello", "world")
Inertia.instance().share([
"hello": "world"
])
NOTE: props
will overwrite shared data.
An extension is provided to ease the initialization of Inertia.
app.registerInertia(version:String, shared:[String: Any])
import Vapor
import VaporInertiaAdapter
import Leaf
// configures your application
public func configure(_ app: Application) throws {
app.http.server.configuration.hostname = "0.0.0.0"
app.http.server.configuration.port = 8080
app.views.use(.leaf)
// serve files from /Public folder
let file = FileMiddleware(publicDirectory: app.directory.publicDirectory)
app.middleware.use(file)
// Defined in mix.swift generated by vapor.mix.js
app.registerLaravelMix()
let version = Environment.get("MIX_ASSETS_VERSION") ?? LaravelMixAssets.version
// Defined in Application+Inertia.swift
// Shared properties are available to all Components
// They are public so be mindful about the size and security of the data sent
// on each response
app.registerInertia(version:version, shared:[
// Probably you would need a better routes to dict converter method
// this is used to generate the links to the different routes for the js view
// The `uri` attribute must be the same as the route in routes.swift
"routes": [
"home": [
"uri": "/",
"methods": ["GET"]
],
"hello": [
"uri": "/hello",
"methods": ["GET"]
]
],
// Container of flash data. Normally using Sessions
"flash": [
"success": [],
"error": []
]
])
// register routes
try routes(app)
}
This adapter follows the Inertia Protocol.
The very first request to an Inertia app is just a regular full-page browser request, with no special Inertia headers or data. For these requests, the server returns a full HTML document.
The adapter uses Leaf to render the Resources/Views/index.leaf and pass the json variable.
Use #inertia()
to render the Inertia root element. If you want more custom element
you can use the json
variable.
<div id='app' data-page='\(json)'></div>
Example Resources/Views/index.leaf
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0'>
<title>Hello World</title>
<!-- #mix provided by vapor.mix.js generator -->
<link href='#mix("/css/app.css")' rel='stylesheet'>
<script src='#mix("/js/app.js")' defer></script>
</head>
<body>
#inertia()
</body>
</html>
Once an Inertia app has been booted, all subsequent requests to the site are made via XHR with a special ?X-Inertia header set to true. This header indicates that the request is being made by Inertia, and isn't a standard full-page visit.
When the server detects the X-Inertia header, instead of responding with a full HTML document, it returns a JSON response with an encoded page object.
Returning an Inertia Response is as simple as using the inertiaRender
function of the request
object.
public func inertiaRender(_ component: String, _ properties: [String:Any]) -> EventLoopFuture<Response>
For rendering inside the request object. request.inertiaRender(component, props)
import Vapor
struct IndexController: RouteCollection {
func boot(routes: RoutesBuilder) throws {
routes.get(use: index)
}
func index(_ req: Request) -> EventLoopFuture<Response> {
return req.inertiaRender(
// This is a Component stored in Resources/js. You must use Webpack or other bundler
// in order to reference the component properly
"Home/Index",
// We pass the properties. All the keys and values must be encodable to a JSON object string
[ "hello": "world"]
)
}
}