Version: 6.99 | License: MIT | Author: Poon Yip Hoon (Aeric)
Pakai Server is a full-stack B4J project template for building modern web applications and REST APIs. It generates all HTML server-side using B4X code (no client-side rendering), leverages HTMX for dynamic interactions without custom JavaScript, and includes both a Web UI and a REST API with auto-generated OpenAPI 3.0 documentation and Swagger UI.
Forum thread: https://www.b4x.com/android/forum/threads/web-project-template-pakai-server-v6.169224/
GitHub: https://github.com/pyhoon/pakai-server-b4j Tutorial: Pakai Framework v6.99 Tutorial
Libraries Guide: Dependency Libraries Guide
- Three deployment variants — Full Stack (Web + API), API-only, Web-only
- HTMX 2.0.8 frontend — no custom JavaScript for CRUD; server returns HTML fragments
- Bootstrap 5.3.8 responsive UI with modal dialogs and toast notifications
- AlpineJS 3.15.8 interactive API console on the documentation page
- REST API — JSON and XML payload support with full CRUD operations
- Auto-generated API docs —
/helppage with interactive console - OpenAPI 3.0 JSON spec generation at
/help?format=openapi - B4X code snippets generation at
/help?format=snippets - Swagger UI integration
- Multi-database — SQLite (default), MySQL, MariaDB (compile-time switching)
- Server-side HTML generation via MiniHtml B4X library (DSL-like builder)
- HTML caching for performance optimization
- SSL/HTTPS support with automatic HTTP-to-HTTPS redirect
- SQLite WAL mode for better concurrent read performance
- Connection pooling for MySQL/MariaDB
- Input validation and duplicate-conflict checking at both UI and API levels
| Template | Modules | Description |
|---|---|---|
Pakai Server (6.99).b4xtemplate |
15 | Full stack: Web UI + REST API + Documentation |
Pakai Server Api (6.99).b4xtemplate |
9 | API only: REST endpoints + HelpHandler as landing page |
Pakai Server Web (6.99).b4xtemplate |
11 | Web only: HTMX frontend without REST API |
| Configuration | Compile Symbol | Database |
|---|---|---|
| Default | hu2_acceptall |
SQLite |
| MariaDB | MariaDB,hu2_acceptall |
MariaDB |
| MySQL | MySQL,hu2_acceptall |
MySQL |
- B4J IDE (version 10.5 or later)
- Java JDK 19+ (or compatible)
- Install B4J IDE from https://www.b4x.com/b4j.html
- Download libraries — click the
GetLibrariesmacro button in the IDE (useslibget.jar) or manually download the dependencies listed below - Open a project file in B4J:
source/Pakai-Server.b4j— Full stacksource/Pakai-Server-Api.b4j— API onlysource/Pakai-Server-Web.b4j— Web only
- Select build configuration — Default (SQLite), MariaDB, or MySQL
- Press F5 (Run) — the server starts
- Open a browser to
http://127.0.0.1:8080
On first run, the application auto-creates the database file (pakai.db for SQLite), creates the tbl_categories and tbl_products tables, and populates them with seed data (2 categories, 3 products).
SQLite (Objects/sqlite.ini):
DbType=SQLite
DbDir=
DbFile=pakai.dbMariaDB (Objects/mariadb.ini):
DbType=MariaDB
DbHost=localhost
DbPort=
DbName=pakai
User=root
Password=password
DriverClass=org.mariadb.jdbc.Driver
JdbcUrl=jdbc:mariadb://{DbHost}:{DbPort}/{DbName}?characterEncoding=utf8&useSSL=false
MaxPoolSize=0MySQL — create Objects/mysql.ini following the same pattern as mariadb.ini.
When the database is configured for MariaDB or MySQL, the application auto-creates the schema (database) if it does not exist.
APP_TITLE=Pakai
APP_TRADEMARK=PAKAI
HOME_TITLE=PAKAI FRAMEWORK
APP_COPYRIGHT=Copyright Computerise System Solutions 2026
ROOT_URL=http://127.0.0.1
ROOT_PATH=
PORT=8080
REDIRECT_TO_HTTPS=False
SSL_PORT=8888
SSL_KEYSTORE_FILE=keystore.jks
SSL_KEYSTORE_PASSWORD=password
SSL_ENABLED=False| Key | Default | Description |
|---|---|---|
APP_TITLE |
Pakai | Browser tab title |
APP_TRADEMARK |
PAKAI | Navbar brand text |
HOME_TITLE |
PAKAI FRAMEWORK | Page heading |
APP_COPYRIGHT |
(see file) | Footer copyright text |
ROOT_URL |
http://127.0.0.1 | Public server URL (use domain in production) |
ROOT_PATH |
(empty) | URL sub-path prefix |
PORT |
8080 | HTTP listen port |
REDIRECT_TO_HTTPS |
False | Auto-redirect HTTP to HTTPS |
SSL_PORT |
8888 | HTTPS listen port |
SSL_KEYSTORE_FILE |
keystore.jks | JKS keystore filename |
SSL_KEYSTORE_PASSWORD |
password | Keystore password |
SSL_ENABLED |
False | Enable HTTPS |
SMTP email settings are also available for notification features.
The application follows a Model-View-Handler pattern:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Handler │ ──> │ Model │ ──> │ Database │
│ (Routing) │ │ (Business) │ │ (MiniORM) │
├──────────────┤ ├──────────────┤ └──────────────┘
│ - Web Handler│ │ - CRUD │
│ - API Handler│ │ - Validation │
│ - Find/Help │ │ - Search │
└──────────────┘ └──────────────┘
│ │
v v
┌──────────────┐ ┌──────────────┐
│ View │ │ Json/XML │
│ (MiniHtml) │ │ (WebApiUtils)│
└──────────────┘ └──────────────┘
| Module | Type | Purpose |
|---|---|---|
ORM.bas |
Static Code | Database init, table creation, seed data |
MC.bas |
Static Code | MiniHtml caching utility |
MH.bas |
Static Code | MiniHtml helper methods |
MainView.bas |
Class | Main HTML layout (Bootstrap navbar, footer, containers) |
CategoriesModel.bas |
Class | tbl_categories CRUD operations |
CategoriesView.bas |
Class | Categories HTML rendering (table, modals) |
CategoriesHandler.bas |
Class | Web handler for /categories, /hx/categories/* |
CategoriesApiHandler.bas |
Class | REST API handler for /api/categories/* |
ProductsModel.bas |
Class | tbl_products CRUD with JOIN and search |
ProductsView.bas |
Class | Products HTML rendering (table, search, modals) |
ProductsHandler.bas |
Class | Web handler for /, /hx/products/* |
ProductsApiHandler.bas |
Class | REST API handler for /api/products/* |
FindApiHandler.bas |
Class | Search/filter API for products |
HelpHandler.bas |
Class | Auto-generated API docs + OpenAPI 3.0 spec |
HttpsFilter.bas |
Class | SSL redirect filter |
| Column | Type | Constraints |
|---|---|---|
| id | INTEGER | Auto-increment, Primary Key |
| category_name | TEXT | NOT NULL |
| created_date | TEXT | Timestamp |
| modified_date | TEXT | Timestamp |
| Column | Type | Constraints |
|---|---|---|
| id | INTEGER | Auto-increment, Primary Key |
| category_id | INTEGER | NOT NULL, FK → tbl_categories.id |
| product_code | TEXT | NOT NULL, length 12 |
| product_name | TEXT | NOT NULL |
| product_price | DECIMAL(10,2) | Default 0.00 |
| product_image | BLOB | Optional |
| created_date | TEXT | Timestamp |
| modified_date | TEXT | Timestamp |
Categories: Hardwares, Toys
Products: Teddy Bear (Toys, $99.90), Hammer (Hardwares, $15.75), Optimus Prime (Toys, $1000.00)
| Method | Path | Handler | Description |
|---|---|---|---|
| GET | / |
ProductsHandler | Product listing page with search |
| GET | /categories |
CategoriesHandler | Category listing page |
| GET | /hx/products/table |
ProductsHandler | Product table HTML (search results) |
| GET | /hx/products/add |
ProductsHandler | Add product modal dialog |
| GET | /hx/products/edit/{id} |
ProductsHandler | Edit product modal dialog |
| GET | /hx/products/delete/{id} |
ProductsHandler | Delete confirmation modal |
| POST | /hx/products/* |
ProductsHandler | Create product |
| PUT | /hx/products/* |
ProductsHandler | Update product |
| DELETE | /hx/products/* |
ProductsHandler | Delete product |
| GET | /hx/categories/table |
CategoriesHandler | Categories table HTML |
| GET | /hx/categories/add |
CategoriesHandler | Add category modal dialog |
| GET | /hx/categories/edit/{id} |
CategoriesHandler | Edit category modal dialog |
| GET | /hx/categories/delete/{id} |
CategoriesHandler | Delete confirmation modal |
| POST | /hx/categories/* |
CategoriesHandler | Create category |
| PUT | /hx/categories/* |
CategoriesHandler | Update category |
| DELETE | /hx/categories/* |
CategoriesHandler | Delete category |
| Method | Path | Handler | Description |
|---|---|---|---|
| GET | /api/products |
ProductsApiHandler | List all products |
| GET | /api/products/{id} |
ProductsApiHandler | Get product by ID |
| POST | /api/products |
ProductsApiHandler | Create product |
| PUT | /api/products/{id} |
ProductsApiHandler | Update product |
| DELETE | /api/products/{id} |
ProductsApiHandler | Delete product |
| GET | /api/categories |
CategoriesApiHandler | List all categories |
| GET | /api/categories/{id} |
CategoriesApiHandler | Get category by ID |
| POST | /api/categories |
CategoriesApiHandler | Create category |
| PUT | /api/categories/{id} |
CategoriesApiHandler | Update category |
| DELETE | /api/categories/{id} |
CategoriesApiHandler | Delete category |
| GET | /api/find |
FindApiHandler | List all products (alias) |
| POST | /api/find |
FindApiHandler | Search products by keyword |
| GET | /api/find/products-by-category_id/{id} |
FindApiHandler | Get products by category |
| Method | Path | Description |
|---|---|---|
| GET | /help |
Interactive API documentation page |
| GET | /help?format=openapi |
OpenAPI 3.0 JSON specification |
| GET | /help?format=snippets |
B4X code snippets for all endpoints |
List all products
GET /api/productsResponse:
{
"code": 200,
"data": [
{"id": 1, "category_id": 2, "category_name": "Toys", "product_code": "T001", "product_name": "Teddy Bear", "product_price": 99.9},
{"id": 2, "category_id": 1, "category_name": "Hardwares", "product_code": "H001", "product_name": "Hammer", "product_price": 15.75},
{"id": 3, "category_id": 2, "category_name": "Toys", "product_code": "T002", "product_name": "Optimus Prime", "product_price": 1000}
]
}Get product by ID
GET /api/products/1{
"code": 200,
"data": {"id": 1, "category_id": 2, "product_code": "T001", "product_name": "Teddy Bear", "product_price": 99.9}
}Create a product
POST /api/products
Content-Type: application/json
{
"category_id": 1,
"product_code": "H002",
"product_name": "Screwdriver",
"product_price": 8.50
}{
"code": 201,
"message": "Product created successfully",
"data": {"id": 4, "category_id": 1, "product_code": "H002", "product_name": "Screwdriver", "product_price": 8.5}
}Update a product
PUT /api/products/4
Content-Type: application/json
{
"category_id": 1,
"product_code": "H002",
"product_name": "Screwdriver Set",
"product_price": 12.00
}{
"code": 200,
"message": "Product updated successfully",
"data": {...}
}Delete a product
DELETE /api/products/4{
"code": 200,
"message": "Product deleted successfully"
}List all categories
GET /api/categories{
"code": 200,
"data": [
{"id": 1, "category_name": "Hardwares"},
{"id": 2, "category_name": "Toys"}
]
}Create a category
POST /api/categories
Content-Type: application/json
{"category_name": "Electronics"}{
"code": 201,
"message": "Category created successfully",
"data": {"id": 3, "category_name": "Electronics"}
}Search products by keyword
POST /api/find
Content-Type: application/json
{"keyword": "bear"}{
"code": 200,
"data": [
{"id": 1, "category_id": 2, "category_name": "Toys", "product_code": "T001", "product_name": "Teddy Bear", "product_price": 99.9}
]
}Products by category
GET /api/find/products-by-category_id/1{
"code": 200,
"data": [
{"id": 2, "category_id": 1, "category_name": "Hardwares", "product_code": "H001", "product_name": "Hammer", "product_price": 15.75}
]
}| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad request (missing required field, invalid ID) |
| 404 | Not found |
| 409 | Conflict (duplicate product code or category name) |
| 422 | Unprocessable entity (database error, invalid payload) |
{
"code": 400,
"error": "Key 'category_name' not found"
}The API supports both JSON and XML payloads. Set
Content-Type: application/xmlto send XML. Response format is determined by the client'sAcceptheader or the server's default configuration.
| Library | Version | Purpose |
|---|---|---|
| EndsMeet | 2.20 | Web framework (routing, HTTP server, sessions, SSL) |
| MiniCSS | 0.30 | CSS generation from B4X code |
| MiniHtml | 3.00 | HTML generation (DSL-like builder) |
| MiniJS | 0.60 | JavaScript generation from B4X code |
| MiniORMUtils | 6.00 | ORM database abstraction layer |
| WebApiUtils | 6.99 | REST API utilities (response formatting, XML/JSON) |
- libget.jar — Auto-download all library dependencies via B4J IDE macro
- resget.jar — Auto-download all static file resources via B4J IDE macro
- modgen.jar — Generate and inject MiniORM code snippets to scaffold models from User-defined Types
- closedb.jar — SQLite Management Utility for closing active connections
| Driver | Version | Database |
|---|---|---|
| sqlite-jdbc-3.7.2.jar | 3.7.2 | SQLite |
| mysql-connector-j-9.3.0.jar | 9.3.0 | MySQL |
| mariadb-java-client-3.5.6.jar | 3.5.6 | MariaDB |
| Library | Version | Used In |
|---|---|---|
| Bootstrap | 5.3.8 | All pages |
| Bootstrap Icons | 1.13.1 | All pages |
| HTMX | 2.0.8 | Web UI pages |
| AlpineJS | 3.15.8 | API documentation page |
pakai-server-b4j/
├── README.md # This file
├── TUTORIAL.md # Step-by-step Pakai Framework tutorial
├── TUTORIAL-LIBRARIES.md # Dependency libraries guide
├── LICENSE # MIT License
├── pakai-index.png # Preview image
├── release/ # Compiled b4xtemplate files
│ ├── Pakai Server (6.99).b4xtemplate
│ ├── Pakai Server Api (6.99).b4xtemplate
│ ├── Pakai Server Web (6.99).b4xtemplate
│ └── swagger.zip # Minimal Swagger UI asset files
└── source/
├── Pakai-Server.b4j # Full stack project file
├── Pakai-Server-Api.b4j # API-only project file
├── Pakai-Server-Web.b4j # Web-only project file
├── $APPNAME$.b4j # Template placeholder
├── libs.json # Library dependency definitions
├── res.json # External resource URLs
├── ORM.bas # Database initialization
├── MainView.bas # Main page layout
├── MC.bas # HTML caching utility
├── MH.bas # MiniHtml helper
├── CategoriesModel.bas # Categories CRUD
├── CategoriesView.bas # Categories HTML views
├── CategoriesHandler.bas # Categories Web handler
├── CategoriesApiHandler.bas # Categories API handler
├── ProductsModel.bas # Products CRUD
├── ProductsView.bas # Products HTML views
├── ProductsHandler.bas # Products Web handler
├── ProductsApiHandler.bas # Products API handler
├── FindApiHandler.bas # Search/filter API
├── HelpHandler.bas # API documentation
├── HttpsFilter.bas # SSL redirect
├── Files/
│ ├── config.example # Server configuration example
│ ├── sqlite.example # SQLite settings example
│ ├── mariadb.example # MariaDB settings example
│ └── mysql.example # MySQL settings example
└── Objects/
├── config.ini # Server configuration (auto-generated)
├── sqlite.ini # SQLite settings (auto-generated)
├── mariadb.ini # MariaDB settings (auto-generated via Conditional Compile)
├── pakai.db # SQLite database (auto-generated)
├── help.html # Generated API documentation page
├── keystore.jks # SSL keystore (generated via JDK keytool)
└── www/ # Static assets
├── assets/css/ # Bootstrap 5.3.8, main.css
├── assets/js/ # Bootstrap, HTMX 2.0.8, AlpineJS 3.15.8, app.js
├── assets/img/ # favicon, sponsor images
└── swagger/ # Swagger UI files
**Note:** Config example files (`config.example`, `sqlite.example`, `mariadb.example`, `mysql.example`) are auto-copied to `Objects/` on first run if the `.ini` file doesn't exist.
- Copy the desired
.b4xtemplatefrom therelease/folder to your B4J Additional Libraries folder - In B4J IDE: File → New → Pakai Server (6.99)
- Set the project name and click OK
- Use the IDE macros (clickable links in the project code header) to:
- GetLibraries — auto-download all B4X library dependencies via
libget.jar - ModelGenerator — scaffold new models from your database schema via
modgen.jar
- GetLibraries — auto-download all B4X library dependencies via
See the Pakai Framework v6.99 Tutorial for a complete step-by-step guide.
If you find this project helpful, consider supporting its development:
Links: Forum | GitHub | PayPal
MIT License — Copyright (c) 2022-2026 Poon Yip Hoon (Aeric). See LICENSE for details.

