Skip to content

Quick Start

This guide walks you through building a small pidgn application with routes, middleware, and JSON responses.

  1. Create a new project

    Terminal window
    pidgn new hello_pidgn
    cd hello_pidgn
  2. Open src/main.zig and replace its contents with:

    const std = @import("std");
    const pidgn = @import("pidgn");
    fn index(ctx: *pidgn.Context) !void {
    ctx.text(.ok, "Hello from pidgn!");
    }
    const App = pidgn.Router.define(.{
    .middleware = &.{pidgn.logger},
    .routes = &.{
    pidgn.Router.get("/", index),
    },
    });
    pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    var server = pidgn.Server.init(gpa.allocator(), .{
    .port = 4000,
    }, &App.handler);
    std.log.info("Listening on http://127.0.0.1:4000", .{});
    try server.listen(std.io.defaultIo());
    }
  3. Run it

    Terminal window
    zig build run

    Visit http://127.0.0.1:4000 — you should see “Hello from pidgn!”.

pidgn ships with 20 built-in middleware. Let’s add a few common ones:

const App = pidgn.Router.define(.{
.middleware = &.{
pidgn.errorHandler(.{ .show_details = true }),
pidgn.logger,
pidgn.requestId(.{}),
pidgn.cors(.{ .allow_origins = &.{"*"} }),
pidgn.bodyParser(.{}),
pidgn.staticFiles(.{ .root = "public", .prefix = "/static" }),
},
.routes = &.{
pidgn.Router.get("/", index),
},
});

Middleware runs in the order listed — put error handling first so it catches everything downstream.

Add a route that returns JSON:

fn getUser(ctx: *pidgn.Context) !void {
const user = .{
.id = 1,
.name = "Alice",
.email = "alice@example.com",
};
try ctx.json(.ok, user);
}
fn createUser(ctx: *pidgn.Context) !void {
const body = try ctx.parseBody(struct {
name: []const u8,
email: []const u8,
});
// ... save to database ...
try ctx.json(.created, body);
}

Wire them into your router:

.routes = &.{
pidgn.Router.get("/", index),
pidgn.Router.get("/users/:id", getUser),
pidgn.Router.post("/users", createUser),
},

For standard CRUD, use Router.resource to generate all routes at once:

.routes = &.{
pidgn.Router.resource("/posts", .{
.index = listPosts,
.show = showPost,
.create = createPost,
.update = updatePost,
.delete_handler = deletePost,
}),
},

This generates GET /posts, GET /posts/:id, POST /posts, PUT /posts/:id, and DELETE /posts/:id.

Group routes under a prefix with shared middleware:

.routes = &.{
pidgn.Router.get("/", index),
pidgn.Router.scope("/api", .{
.middleware = &.{ pidgn.bearerAuth(.{ .validate = &myValidator }) },
}, &.{
pidgn.Router.get("/me", currentUser),
pidgn.Router.resource("/posts", .{
.index = listPosts,
.create = createPost,
}),
}),
},

For a faster development cycle, use the pidgn server command to automatically rebuild on source changes, and enable live reload for instant browser refresh:

Terminal window
pidgn server

If your project uses bundled assets, run pidgn assets watch in a separate terminal. See Asset Pipeline for setup details.

You now have a working pidgn application with routing, middleware, and JSON. From here: