Welcome to Gingee! This guide is your starting point for building powerful, secure, and modern applications on the Gingee platform. We'll take you from a simple "Hello World" to a complete, database-backed RESTful API.
If you haven't already, please read the Core Concepts MD HTML guide for a high-level overview of the platform's architecture.
NOTE: This guide focuses on creating a Gingee backend with the classic Multi-Page App (MPA) workflow. For a complete tutorial on building modern SPAs with frameworks like React, Vue, or Angular, please see our dedicated Gingee SPA Developer's Guide MD HTML
The fastest way to get started is with the gingee-cli
. After you've created your first project with gingee-cli init my-project
, you can create your first application.
cd my-project
gingee-cli add-app first-app
The CLI will scaffold a new, working application in the web/first-app/
directory. Let's look at the three most important files it created:
web/first-app/index.html
: The main HTML page.web/first-app/scripts/cl_app.js
: The client-side JavaScript for the index.html
page.web/first-app/box/hello.js
: A server-side script.Run your server with npm run dev
and navigate to http://localhost:7070/first-app
. You'll see the page and an interactive "Say Hello" button. This works because of Gingee's File-Based Routing:
/first-app
) serves the static index.html
.cl_app.js
makes a fetch
call to /first-app/hello
.web/first-app/box/hello.js
.$g
GlobalLet's look inside web/first-app/box/hello.js
. All Gingee server scripts follow this simple structure:
module.exports = async function() {
await gingee(async ($g) => {
// Your application logic lives here!
$g.response.send({ message: 'Hello from the first-app server script!' });
});
};
module.exports
: Each script is a standard Node.js module that exports a single async
function.await gingee(handler)
: This globally available function is the heart of the system. It wraps your logic, providing security and automatically handling complex tasks like parsing the request body. You should always await
it.$g
: The single, powerful "global" object passed to your handler. It's your secure gateway to everything you need.Let's modify the script to take a query parameter:
// in web/first-app/box/hello.js
await gingee(async ($g) => {
const name = $g.request.query.name || 'World';
$g.response.send({ message: `Hello, ${name}!` });
});
Now, navigate to /first-app/hello?name=Gingee
and you'll see the personalized response. All query parameters are automatically parsed for you in $g.request.query
.
For a full breakdown of all properties on $g
, see the Server Script & $g Object Reference MD HTML
routes.json
While file-based routing is great for simple pages, a real API needs clean, dynamic URLs (e.g., /posts/123
). For this, we use manifest-based routing.
web/first-app/box/routes.json
.web/first-app/box/routes.json
{
"routes": [
{
"path": "/posts",
"method": "POST",
"script": "api/posts/create.js"
},
{
"path": "/posts/:postId",
"method": "GET",
"script": "api/posts/get.js"
}
]
}
Now, a POST
request to /first-app/posts
will execute box/api/posts/create.js
. A GET
request to /first-app/posts/abc-123
will execute box/api/posts/get.js
.
In your script, you can access the dynamic :postId
parameter via $g.request.params
.
web/first-app/box/api/posts/get.js
module.exports = async function() {
await gingee(async ($g) => {
const postId = $g.request.params.postId; // "abc-123"
// ... logic to fetch post from database ...
$g.response.send({ id: postId, title: 'My First Post' });
});
};
Gingee makes database interaction simple and secure.
gingee-cli add-app my-blog
and follow the wizard to configure your database. This will populate the db
section of your web/my-blog/box/app.json
.db
module in your scripts.web/my-blog/box/api/posts/get.js
(with DB logic)
module.exports = async function() {
await gingee(async ($g) => {
const db = require('db');
const postId = $g.request.params.postId;
const DB_NAME = 'main_db'; // The 'name' from your app.json
try {
const sql = 'SELECT * FROM "Posts" WHERE "id" = $1';
const post = await db.query.one(DB_NAME, sql, [postId]);
if (post) {
$g.response.send(post);
} else {
$g.response.send({ error: 'Post not found' }, 404);
}
} catch (err) {
$g.log.error('Failed to fetch post', { postId, error: err.message });
$g.response.send({ error: 'Internal Server Error' }, 500);
}
});
};
db.query.one
: Fetches a single record.db.query.many
: Fetches an array of records.db.execute
: Use for INSERT
, UPDATE
, and DELETE
. Returns the number of rows affected.$1
, $2
) to prevent SQL injection. The db
module handles this securely.Let's secure our POST /posts
endpoint and validate its input.
web/my-blog/box/auth_middleware.js
.
module.exports = async function() {
await gingee(async ($g) => {
const auth = require('auth');
const token = $g.request.headers.authorization?.split(' ')[1];
const payload = auth.jwt.verify(token);
if (!payload) {
// This call ends the request and prevents the main handler from running.
$g.response.send({ error: 'Unauthorized' }, 401);
}
});
};
web/my-blog/box/app.json
, add it to the default_include
array. Now it will run before every script in your app.
"default_include": ["auth_middleware.js"]
create.js
script, use the utils
module.
web/my-blog/box/api/posts/create.js
module.exports = async function() {
await gingee(async ($g) => {
const { validate } = require('utils');
const { title, content } = $g.request.body;
if (validate.isEmpty(title)) {
return $g.response.send({ error: 'Title is required.' }, 400);
}
// ... insert into database ...
});
};
Gingee was co-authored with a Generative AI, and you can leverage this same powerful workflow to build your own applications. The key is to provide the AI with a "knowledge bundle" of the platform's architecture. We've created this for you.
How to Start a Development Session with an AI:
Get the Context File: Locate the pre-built docs/ai-context.md
file in the Gingee repo. This file contains all the core concepts and API references of Gingee that an AI needs.
Start a New Chat: Open a new session with a capable coding AI partner (like Google Gemini).
Use the Priming Prompt: Your very first message should be to upload or paste the entire contents of the ai-context.md
file, preceded by this simple instruction:
You are an expert developer for a Node.js application server called Gingee. Your goal is to help me build a new application on this platform by exclusively using the following context, documentation, and API reference. Analyze it carefully and confirm when you are ready to begin.
[PASTE THE ENTIRE CONTENTS OF ai-context.md HERE]
Give it a Task: Once the AI confirms it has processed the information, you can start giving it high-level tasks.
Example Follow-up Prompt:
"Great. Now, using Gingee, let's build the
create.js
script for our blog. It should handle aPOST
request to/posts
, take a JSON body withtitle
andcontent
, validate thattitle
is not empty and has a max length of 100 characters, insert the new post into ourmain_db
PostgreSQL database, and return the newly created post with its ID."
The AI now has all the context it needs to generate high-quality, secure, and idiomatic Gingee code, dramatically accelerating your development process.
Once you have built and tested your application, Gingee makes it easy to package it for distribution, either publicly or within your organization. A properly packaged app is a self-contained .gin
file that includes all necessary code, assets, and security manifests.
There are three key steps to preparing your app for distribution.
1. Declare Your Permissions (pmft.json
)
This is the most important step for security and user trust. You must declare what protected Gingee modules your app needs.
web/my-app/box/pmft.json
.mandatory
and optional
permissions.web/my-app/box/pmft.json
{
"permissions": {
"mandatory": [
"db",
"fs"
],
"optional": [
"httpclient"
]
}
}
For a full list of permissions, see the Permissions Guide.
2. Control Package Contents (.gpkg
)
Create a .gpkg
manifest in your box
folder to exclude any development-only files (like local SQLite databases or frontend source code in SPA) from the final package.
web/my-app/box/.gpkg
{
"include": ["**/*"],
"exclude": [
"box/data/**",
"dev_src/**",
".gpkg",
"pmft.json"
]
}
3. Create the Package (.gin
)
With the manifests in place, you can now create the final distributable package using the gingee-cli
. This command connects to your running local server to create the package.
# Make sure your local Gingee server is running
npm start
# Package the 'my-app' application
gingee-cli package-app --appName my-app
This will generate a versioned .gin
file (e.g., my-app-v1.2.0.gin
) in your current directory. This single file is all anyone needs to install your application on their own Gingee server using the gingee-cli install-app
command.