serve

A cross-platform HTTP server with built-in routing support.
https://github.com/jeremyfa/serve

To install, run:

haxelib install serve 1.0.0 

See using Haxelib in Haxelib documentation for more information.

README.md

Serve

A lightweight, cross-platform HTTP server library for Haxe with built-in routing support. Compiles to PHP and Node.js with zero dependencies.

Installation

# From Haxelib
haxelib install serve

# From Git (latest)
haxelib git serve https://github.com/jeremyfa/serve.git

Quick Start

// Main.hx
import serve.*;

class Main {
    static function main() {
        var server = new Server(
            #if php
            new PhpBackend()
            #elseif nodejs
            new NodeJsBackend(3000, "localhost")
            #end
        );

        server.add(new MyRouter());
        server.start();
    }
}

// MyRouter.hx
class MyRouter extends serve.Router {
    @get('/')
    function index(req:Request, res:Response) {
        res.json({message: "Hello, World!"});
    }
}

Routing

Define routes using metadata annotations on methods in a Router class:

class UserRouter extends serve.Router {
    @get('/users')
    function listUsers(req:Request, res:Response) {
        res.json({users: []});
    }

    @get('/users/$id')
    function getUser(req:Request, res:Response) {
        var userId = req.params.id;
        res.json({id: userId, name: "User " + userId});
    }

    @post('/users')
    function createUser(req:Request, res:Response) {
        var name = req.body.name;  // JSON body auto-parsed
        res.status(201).json({id: 123, name: name});
    }

    @put('/users/$id')
    function updateUser(req:Request, res:Response) {
        res.json({success: true});
    }

    @delete('/users/$id')
    function deleteUser(req:Request, res:Response) {
        res.status(204).text("");
    }
}

Route Parameters

Use $paramName syntax in routes. Parameters are available in req.params:

@get('/posts/$postId/comments/$commentId')
function getComment(req:Request, res:Response) {
    var postId = req.params.postId;
    var commentId = req.params.commentId;
    // ...
}

API Reference

Request

PropertyTypeDescription
uriStringRequest path (without query string)
methodHttpMethodGET, POST, PUT, DELETE
paramsDynamicRoute parameters
queryDynamicQuery string parameters
bodyDynamicParsed request body (JSON/form)
headersMapHTTP headers (normalized to Title-Case)

Response

MethodDescription
status(code:Int)Set HTTP status code
header(name:String, value:String)Set response header
json(data:Dynamic)Send JSON response
html(content:String)Send HTML response
text(content:String)Send plain text
redirect(url:String)Send 302 redirect
notFound()Send 404 response

All methods return Response for chaining, except terminal methods (json, html, text, redirect).

Platform Usage

PHP

# Build
haxe build-php.hxml

# Run with built-in server
php -S localhost:8000 -t out/php/

# Deploy to any PHP host (Apache, Nginx + PHP-FPM, etc.)

Node.js

# Build
haxe build-nodejs.hxml

# Run
node out/nodejs/server.js

# Or with PM2
pm2 start out/nodejs/server.js

Examples

Complete REST API

class ProductAPI extends serve.Router {
    var products = new Map<String, Dynamic>();

    @get('/api/products')
    function list(req:Request, res:Response) {
        var page = req.query.page != null ? Std.parseInt(req.query.page) : 1;
        var limit = req.query.limit != null ? Std.parseInt(req.query.limit) : 10;

        res.json({
            page: page,
            items: [for (p in products) p],
            total: products.count()
        });
    }

    @get('/api/products/$id')
    function get(req:Request, res:Response) {
        var product = products.get(req.params.id);
        if (product != null) {
            res.json(product);
        } else {
            res.notFound();
        }
    }

    @post('/api/products')
    function create(req:Request, res:Response) {
        var id = Std.string(Date.now().getTime());
        products.set(id, {
            id: id,
            name: req.body.name,
            price: req.body.price
        });
        res.status(201)
           .header("Location", '/api/products/$id')
           .json({id: id});
    }
}

Multiple Routers

var server = new Server(backend);

// Add routers in order - first match wins
server.add(new AuthRouter());     // Authentication endpoints
server.add(new ApiRouter());      // API routes
server.add(new AdminRouter());    // Admin panel
server.add(new PublicRouter());   // Public pages

server.start();

Custom Request Handler

// Add middleware-like functionality
server.add(function(req:Request, res:Response) {
    // Log all requests
    trace('${req.method} ${req.uri}');

    // Add CORS headers
    res.header("Access-Control-Allow-Origin", "*")
       .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");

    // Don't mark route as resolved - continues synchronously to next handler
});

// Add routers after middleware
server.add(new ApiRouter());

Architecture

Backend Interface Pattern

Serve abstracts platform differences behind a Backend interface, allowing the same router code to run on different platforms:

// Your code works everywhere
class MyRouter extends Router {
    @get('/hello')
    function hello(req:Request, res:Response) {
        res.json({platform: #if php "PHP" #else "Node.js" #end});
    }
}

Compile-Time Route Generation

Routes are generated at compile-time using Haxe macros, reducing runtime overhead.

Creating Custom Backends

Implement the Backend interface to support new platforms:

class MyBackend implements Backend {
    var serverHost:String = "localhost";
    var serverPort:Int = 3000;

    public function new(?port:Int = 3000, ?host:String = "localhost") {
        this.serverPort = port;
        this.serverHost = host;
    }

    public function start(server:Server):Void {
        // Start listening for requests
    }

    public function host():String {
        return serverHost;
    }

    public function port():Int {
        return serverPort;
    }

    public function responseStatus(response:Response, status:Int):Void {
        // Set HTTP status
    }

    public function responseHeader(response:Response, name:String, value:String):Void {
        // Set response header
    }

    public function responseText(response:Response, text:String):Void {
        // Send response body
    }
}

Current Limitations

Serve focuses on core HTTP routing. Not included: sessions, auth, static files, templates, WebSockets, file uploads, HTTPS/TLS, rate limiting, compression, or custom error pages.

Custom request handlers used as middleware are currently executed synchronously, preventing them to perform async operation before a route is resolved. This will be improved in a future version.

License

MIT License - see LICENSE file for details.

Contributors
jeremyfa
Version
1.0.0
Published
6 months ago
License
MIT

All libraries are free

Every month, more than a thousand developers use Haxelib to find, share, and reuse code — and assemble it in powerful new ways. Enjoy Haxe; It is great!

Explore Haxe

Haxe Manual

Haxe Code Cookbook

Haxe API documentation

You can try Haxe in the browser! try.haxe.org

Join us on GitHub!

Haxe is being developed on GitHub. Feel free to contribute or report issues to our projects.

Haxe on GitHub