Quick Start¶
Build the same minimal service in each binding. Choose a tab, copy the snippet, and run.
Define routes¶
from spikard import Spikard
from spikard.config import ServerConfig
from msgspec import Struct
class User(Struct):
id: int
name: str
app = Spikard()
@app.get("/users/{id:int}")
async def get_user(id: int) -> User:
return User(id=id, name="Alice")
@app.post("/users")
async def create_user(user: User) -> User:
return user
if __name__ == "__main__":
app.run(config=ServerConfig(port=8000))
import { Spikard, type Request } from "spikard";
import { z } from "zod";
const UserSchema = z.object({ id: z.number(), name: z.string() });
type User = z.infer<typeof UserSchema>;
const app = new Spikard();
app.addRoute(
{ method: "GET", path: "/users/:id", handler_name: "getUser", is_async: true },
async (req: Request): Promise<User> => {
const id = Number(req.params["id"] ?? 0);
return { id, name: "Alice" };
},
);
app.addRoute(
{
method: "POST",
path: "/users",
handler_name: "createUser",
request_schema: UserSchema,
response_schema: UserSchema,
is_async: true,
},
async (req: Request): Promise<User> => UserSchema.parse(req.json()),
);
if (require.main === module) {
app.run({ port: 8000 });
}
<?php
declare(strict_types=1);
use Spikard\App;
use Spikard\Attributes\Get;
use Spikard\Attributes\Post;
use Spikard\Config\ServerConfig;
use Spikard\Http\Request;
use Spikard\Http\Response;
final class UsersController
{
#[Get('/users')]
public function list(): Response
{
return Response::json([
'users' => [
['id' => 1, 'name' => 'Alice'],
['id' => 2, 'name' => 'Bob'],
]
]);
}
#[Get('/users/{id}')]
public function show(Request $request): Response
{
$userId = (int) $request->pathParams['id'];
return Response::json(['id' => $userId, 'name' => 'Alice']);
}
#[Post('/users')]
public function create(Request $request): Response
{
$user = $request->body;
return Response::json($user, 201);
}
}
$app = (new App(new ServerConfig(port: 8000)))
->registerController(new UsersController());
$app->run();
use axum::response::Json;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use spikard::{get, post, App, RequestContext};
#[derive(Serialize, Deserialize, JsonSchema)]
struct User {
id: i64,
name: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut app = App::new();
app.route(get("/users/:id"), |ctx: RequestContext| async move {
let id = ctx.path_param("id").unwrap_or("0").parse::<i64>().unwrap_or_default();
Ok(Json(User { id, name: "Alice".into() }).into())
})?;
app.route(
post("/users").request_body::<User>().response_body::<User>(),
|ctx: RequestContext| async move {
let user: User = ctx.json()?;
Ok(Json(user).into())
},
)?;
app.run().await?;
Ok(())
}
Run it¶
<?php
declare(strict_types=1);
use Spikard\App;
use Spikard\Attributes\Get;
use Spikard\Config\ServerConfig;
use Spikard\Http\Response;
final class HealthController
{
#[Get('/health')]
public function health(): Response
{
return Response::json(['status' => 'ok']);
}
}
$config = ServerConfig::builder()
->withHost('0.0.0.0')
->withPort(8000)
->withWorkers(4)
->build();
$app = (new App($config))
->registerController(new HealthController());
$app->run();
Next steps¶
- Add middleware (logging, auth, tracing) with the same signature in every binding.
- Wire JSON Schema validation so request/response contracts stay enforced.
- Deploy using the Rust binary, the CLI, or container images (see Deployment).