Lloople / vapor-inertia-adapter

The Vapor framework adapter for Inertia.js

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Vapor Inertia Adapter

Vapor Logo Swift 5.2 Logo Build Status MIT License

This package is meant to help you using Inertia JS in your Vapor 4 project.

Features

Installation

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")
        ]),
    // ...
]

Usage

Middleware

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())

Version

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.

Shared Data

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.

app.registerInertia()

An extension is provided to ease the initialization of Inertia.

app.registerInertia(version:String, shared:[String: Any])

Example configure.swift

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)
}

Creating responses

This adapter follows the Inertia Protocol.

Leaf Rendering

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>

Inertia JSON Rendering

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.

Sample Controller

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"]
        )
    }
}

About

The Vapor framework adapter for Inertia.js

License:MIT License


Languages

Language:Swift 78.7%Language:JavaScript 21.3%