Realtime chat example where the server module source-of-truth is Haxe, compiled to Phoenix LiveView + Presence.
This example is the Haxe-first counterpart to examples/12-phoenix-chat/:
12-phoenix-chat: hybrid adoption shape (feature logic in Haxe, core Phoenix wiring hand-authored Elixir)15-phoenix-chat-haxe-first: app/router/live/presence authored in Haxe
@:applicationsupervision tree authored in Haxe@:routermodule-level typed routes authored in Haxe@:liveviewcallbacks and render in Haxe@:presencemodule in Haxe- Haxe->JS client hook (
AutoScroll) for LiveView - TSX-style HXX templates (
-D hxx_mode=tsx)
cd examples/15-phoenix-chat-haxe-first
mix setup
mix phx.serverOpen http://localhost:4000.
cd examples/15-phoenix-chat-haxe-first
mix testThe example includes a Haxe-authored ExUnit test:
examples/15-phoenix-chat-haxe-first/src_haxe/test/live/ChatStateTest.hx
examples/15-phoenix-chat-haxe-first/src_haxe/PhoenixChat.hx@:applicationsupervision tree (PhoenixChat.Application)
examples/15-phoenix-chat-haxe-first/src_haxe/PhoenixChatRouter.hx@:routermodule-level route DSL (PhoenixChatWeb.Router)
examples/15-phoenix-chat-haxe-first/src_haxe/phoenix_chat_hx/live/AppLive.hx@:liveviewcallbacks + render (PhoenixChatWeb.AppLive)
examples/15-phoenix-chat-haxe-first/src_haxe/phoenix_chat_hx/presence/ChatPresence.hx@:presencemodule (PhoenixChatWeb.Presence)
- Phoenix config/runtime wiring:
examples/15-phoenix-chat-haxe-first/config/*.exs
- Phoenix web infrastructure scaffold:
examples/15-phoenix-chat-haxe-first/lib/phoenix_chat_web/endpoint.exexamples/15-phoenix-chat-haxe-first/lib/phoenix_chat_web.exexamples/15-phoenix-chat-haxe-first/lib/phoenix_chat_web/components/**
Why this split exists: Phoenix project/config conventions are already Elixir-native and stable, while app behavior and framework modules can be type-checked and authored in Haxe.
Application (@:application):
@:application
@:appName("PhoenixChat")
class PhoenixChat {
public static function start(type: ApplicationStartType, args: ApplicationArgs): ApplicationResult {
var children:Array<ChildSpecFormat> = [
TypeSafeChildSpec.telemetry(Telemetry),
TypeSafeChildSpec.moduleWithConfig(DNSCluster, [{key: "query", value: dnsClusterQuery}]),
TypeSafeChildSpec.pubSub(PubSub),
TypeSafeChildSpec.moduleRef(ChatPresence),
TypeSafeChildSpec.endpoint(Endpoint)
];
return SupervisorExtern.startLink(children, options);
}
}Why this shape:
- catches unresolved module refs at compile time
- keeps supervision-tree code compact and explicit
- preserves standard OTP child-spec runtime output
For pure Elixir modules, this example adds small extern wrappers under src_haxe/phoenix_chat_hx/infrastructure/ (for example @:native("PhoenixChat.PubSub") @:unsafeExtern extern class PubSub {}) so callsites stay typed.
Router (@:router module-level field):
import reflaxe.elixir.macros.RouterDsl.*;
@:native("PhoenixChatWeb.Router")
@:router
final routes = [
pipeline(browser, [
plug(accepts, {initArgs: ["html"]}),
plug(fetch_session),
plug(fetch_live_flash),
plug(protect_from_forgery),
plug(put_secure_browser_headers)
]),
scope("/", [
pipeThrough([browser]),
liveSession("default", [live("/", AppLive)])
])
];Note: live routes can omit actions (live("/", AppLive)), which keeps LiveView modules free of placeholder index/show/edit methods when you do not need route-action dispatch.
examples/15-phoenix-chat-haxe-first/build.hxml-D elixir_output=libso Haxe-generated server modules land in standard Phoenixlib/**paths.- Includes app + router + live/presence modules in the compile roots.
- Haxe-first walkthrough:
docs/06-guides/PHOENIX_CHAT_TUTORIAL_HAXE_FIRST.md - Hybrid walkthrough:
docs/06-guides/PHOENIX_CHAT_TUTORIAL.md - New app setup:
docs/06-guides/PHOENIX_NEW_APP.md - Type-safe child specs:
docs/04-api-reference/TYPE_SAFE_CHILD_SPEC.md