# Spikard Elixir
[](https://spikard.dev)
[](https://crates.io/crates/spikard)
[](https://pypi.org/project/spikard/)
[](https://www.npmjs.com/package/@spikard/node)
[](https://rubygems.org/gems/spikard)
[](https://packagist.org/packages/spikard/spikard)
[](https://hex.pm/packages/spikard)
[](LICENSE)
Elixir bindings for Spikard: a Rust-centric web framework with type-safe code generation from OpenAPI, GraphQL, AsyncAPI, and OpenRPC specifications. Leverage Phoenix-style routing with zero-copy Rustler NIF performance.
## Key Features
- **Phoenix-style routing**: Compile-time `get`, `post`, `put`, `patch`, `delete` macros with scopes
- **Zero-copy NIFs**: Rustler bindings eliminate serialization overhead
- **Full typespecs**: `@spec` annotations on all public functions for Dialyzer type checking
- **Code generation**: Generate type-safe handlers from OpenAPI, GraphQL, AsyncAPI, and OpenRPC specs
- **Tower-HTTP middleware**: Compression, rate limiting, authentication, CORS, request IDs
- **Real-time**: WebSockets and Server-Sent Events (SSE)
- **Production-ready**: Dependency injection, validation schemas, lifecycle hooks, background tasks
## Installation
Add `spikard` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:spikard, "~> 0.11"}
]
end
```
**Requirements:** Elixir 1.18+ with OTP 27+, and a Rust toolchain (for compiling NIFs from source).
## Quick Start
```elixir
defmodule MyApp.Router do
use Spikard.Router
get "/", &hello/1
get "/users/:id", &show_user/1
post "/users", &create_user/1
defp hello(_request) do
Spikard.Response.json(%{message: "Hello, World!"})
end
defp show_user(request) do
user_id = Spikard.Request.get_path_param(request, "id")
Spikard.Response.json(%{id: user_id, name: "Alice"})
end
defp create_user(request) do
body = Spikard.Request.get_body(request)
Spikard.Response.json(%{id: 1, name: body["name"]}, status: 201)
end
end
{:ok, server} = Spikard.start(MyApp.Router, port: 4000)
```
The `Spikard.Request` struct provides access to:
- `get_path_param(request, key)` - Path parameters
- `get_query_param(request, key, default)` - Query parameters
- `get_header(request, name)` - Request headers (case-insensitive)
- `get_cookie(request, name)` - Request cookies
- `get_body(request)` - Parsed request body
- `files(request)` - Uploaded files
## Responses
Return maps, or use the fluent `Response` builder:
```elixir
# Simple JSON (auto-serialized)
Spikard.Response.json(%{message: "Hello"})
# Custom status and headers
Spikard.Response.new()
|> Spikard.Response.with_status(201)
|> Spikard.Response.with_json(%{id: 1})
|> Spikard.Response.with_header("x-request-id", "abc123")
|> Spikard.Response.with_cookie("session", "xyz", http_only: true, secure: true)
# Streaming response
Spikard.Response.stream(Stream.interval(1000) |> Stream.map(&"tick #{&1}"))
```
## Configuration
Configure the server with middleware options:
```elixir
{:ok, server} = Spikard.start(MyApp.Router,
port: 4000,
host: "0.0.0.0",
config: [
compression: %{gzip: true, brotli: true},
rate_limit: %{per_second: 100},
cors: %{
allowed_origins: ["https://example.com"],
allowed_methods: ["GET", "POST", "OPTIONS"],
allowed_headers: ["content-type", "authorization"],
max_age: 3600
},
jwt: %{secret: "your-secret", algorithm: "HS256"},
static_files: %{directory: "./priv/static"}
]
)
```
## Dependency Injection
Inject dependencies into handlers:
```elixir
db = %{host: "localhost", port: 5432}
{:ok, server} = Spikard.start(MyApp.Router,
port: 4000,
dependencies: [
Spikard.DI.value("database", db),
Spikard.DI.factory("request_id", fn -> System.unique_integer() end, singleton: false)
]
)
# In handler:
def show(request) do
db = Spikard.Request.get_dependency(request, "database")
Spikard.Response.json(%{host: db.host})
end
```
## Lifecycle Hooks
Execute logic at key points in the request lifecycle:
```elixir
auth_hook = fn ctx ->
case Map.get(ctx.headers, "authorization") do
"Bearer " <> _token -> {:continue, ctx}
_ -> {:short_circuit, %{status: 401, body: %{error: "Unauthorized"}}}
end
end
{:ok, server} = Spikard.start(MyApp.Router,
port: 4000,
lifecycle: [
on_request: [fn ctx -> IO.inspect(ctx.path); {:continue, ctx} end],
pre_handler: [auth_hook],
on_response: [fn res -> {:continue, res} end]
]
)
```
Hooks can return `{:continue, context}` to proceed or `{:short_circuit, response}` to respond immediately.
## Real-Time Communication
**WebSockets:**
```elixir
defmodule ChatHandler do
use Spikard.WebSocket
def handle_connect(_ws, _opts), do: {:ok, %{}}
def handle_message(msg, state), do: {:reply, msg, state}
def handle_disconnect(_ws, _state), do: :ok
end
# In router:
websocket "/chat", ChatHandler
```
**Server-Sent Events:**
```elixir
defmodule TickProducer do
use Spikard.Sse.Producer
def init(_opts), do: {:ok, 0}
def next_event(count) when count < 100 do
{:ok, %Spikard.Sse.Event{data: count, event: "tick"}, count + 1}
end
def next_event(_), do: :done
end
# In router:
sse "/events", TickProducer
```
## Background Tasks
Offload work after sending response:
```elixir
def process(request) do
Spikard.Background.run(fn -> send_notification_email() end)
Spikard.Response.json(%{status: "processing"})
end
```
## File Uploads
Handle multipart file uploads:
```elixir
def upload(request) do
case Spikard.Request.files(request) do
[file | _] ->
Spikard.Response.json(%{
filename: file.filename,
size: file.size,
is_image: Spikard.UploadFile.image?(file)
})
[] ->
Spikard.Response.json(%{error: "No file uploaded"}, status: 400)
end
end
```
## Testing
Use the `TestClient` for integration tests without network overhead:
```elixir
defmodule MyAppTest do
use ExUnit.Case
test "GET / returns hello" do
{:ok, client} = Spikard.TestClient.new(routes: MyApp.Router.routes())
{:ok, response} = Spikard.TestClient.get(client, "/")
assert response.status_code == 200
assert Spikard.TestClient.Response.json(response) == %{"message" => "Hello, World!"}
end
end
```
## Learn More
**Examples & Code Generation:**
- [Runnable Examples](../../examples/) - Elixir, Python, TypeScript, Ruby, PHP, and Rust
- [Code Generation Guide](../../examples/README.md) - Generate from OpenAPI, GraphQL, AsyncAPI, OpenRPC
**Documentation:**
- Full documentation at [spikard.dev](https://spikard.dev)
- [HexDocs](https://hexdocs.pm/spikard) - Elixir API reference
- [CONTRIBUTING.md](../../CONTRIBUTING.md) - Development workflow
**Other Languages:**
- [Python (PyPI)](https://pypi.org/project/spikard/)
- [TypeScript (npm)](https://www.npmjs.com/package/@spikard/node)
- [Ruby (RubyGems)](https://rubygems.org/gems/spikard)
- [PHP (Packagist)](https://packagist.org/packages/spikard/spikard)
- [Rust (Crates.io)](https://crates.io/crates/spikard)
## License
MIT - See [LICENSE](LICENSE) for details