Getting Started with Gati
Welcome to Gati - a next-generation TypeScript/Node.js framework for building cloud-native, versioned APIs with automatic scaling, deployment, and SDK generation.
Table of Contents
- Prerequisites
- Installation
- Quick Start
- Project Structure
- Basic Concepts
- Your First API Endpoint
- Running Your Application
- Next Steps
- Troubleshooting
Prerequisites
Before you begin, ensure you have the following installed:
- Node.js >= 18.0.0
- pnpm >= 8.0.0 (recommended) or npm >= 8.0.0
Check your versions:
node --version # Should be >= 18.0.0
pnpm --version # Should be >= 8.0.0If you don't have pnpm installed:
npm install -g pnpmInstallation
Option 1: Using the CLI (Recommended)
Install the CLI globally (recommended):
npm install -g @gati-framework/cli
gatic create my-app
cd my-appNotes:
- We provide a distinct command name
gaticto avoid collisions with other OSS projects. - You can still run without global install using
npx gatic <command>if preferred.
Option 2: Clone the Hello World Example
git clone https://github.com/krishnapaul242/gati.git
cd gati/examples/hello-world
pnpm installQuick Start
Let's build your first Gati application in under 15 minutes!
Step 1: Create a New Project
gatic create my-first-app
cd my-first-app
pnpm installStep 2: Understand the Project Structure
Your new project looks like this:
my-first-app/
├── src/
│ ├── handlers/ # Request handlers
│ │ └── hello.ts # Example handler
│ └── modules/ # Reusable modules
│ └── logger.ts # Example module
├── gati.config.ts # Application configuration
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript configuration
└── .env.example # Environment variables templateStep 3: Examine Your First Handler
Open src/handlers/hello.ts:
import type { Handler } from 'gati';
export const helloHandler: Handler = (req, res, gctx, lctx) => {
res.json({
message: 'Hello, World!',
timestamp: lctx.timestamp,
requestId: lctx.requestId,
});
};This simple handler demonstrates the core handler signature:
req- HTTP request objectres- HTTP response objectgctx- Global context (shared across all requests)lctx- Local context (specific to this request)
Step 4: Configure Your Routes
Open gati.config.ts:
import { helloHandler } from './src/handlers/hello';
export default {
server: {
port: 3000,
host: 'localhost',
},
routes: [
{
method: 'GET',
path: '/hello',
handler: helloHandler,
},
],
modules: (gctx) => {
// Initialize modules here
},
};Step 5: Start the Development Server
pnpm devYou should see:
Gati server listening on http://localhost:3000Step 6: Test Your API
In another terminal:
curl http://localhost:3000/helloResponse:
{
"message": "Hello, World!",
"timestamp": 1699564800000,
"requestId": "req_abc123"
}🎉 Congratulations! You've created your first Gati API endpoint!
Project Structure
Let's understand each part of your Gati application:
Handlers (src/handlers/)
Handlers are functions that process HTTP requests. Each handler follows this signature:
handler(req, res, gctx, lctx)- Purpose: Process requests and generate responses
- Example:
getUserHandler,createPostHandler - Learn more: Handler Development Guide
Modules (src/modules/)
Modules are reusable business logic components loaded via dependency injection:
export function initLogger(gctx: GlobalContext): Logger {
return {
log: (message) => console.log(message),
error: (message, error) => console.error(message, error),
};
}- Purpose: Shared functionality (database, cache, logger)
- Pattern: Initialized once, shared across requests
- Learn more: Module Creation Guide
Configuration (gati.config.ts)
The main configuration file for your application:
export default {
server: {
port: 3000, // Server port
host: 'localhost', // Server host
},
routes: [/* ... */], // Route definitions
modules: (gctx) => {}, // Module initialization
config: {/* ... */}, // App-level config
};Basic Concepts
1. Handlers
Handlers are pure functions that process requests:
const getUserHandler: Handler = async (req, res, gctx, lctx) => {
const userId = req.params.id;
const user = await gctx.modules['db'].findUser(userId);
res.json({ user });
};Key Points:
- Synchronous or asynchronous
- Access request data via
req - Send responses via
res - Use modules via
gctx.modules - Access request metadata via
lctx
2. Modules
Modules provide shared functionality:
// src/modules/database.ts
export function initDatabase(gctx: GlobalContext) {
const connection = createConnection(/* ... */);
gctx.lifecycle.onShutdown(() => {
connection.close();
});
return {
findUser: (id) => connection.query('SELECT * FROM users WHERE id = ?', [id]),
};
}
// gati.config.ts
modules: (gctx) => {
gctx.modules['db'] = initDatabase(gctx);
}Key Points:
- Initialized once at startup
- Shared across all requests
- Support lifecycle hooks (init, shutdown)
- Accessed via
gctx.modules
3. Context
Gati provides two types of context:
Global Context (gctx) - Shared across all requests:
gctx.modules- Module registrygctx.config- Application configurationgctx.state- Shared stategctx.lifecycle- Lifecycle hooks
Local Context (lctx) - Scoped to a single request:
lctx.requestId- Unique request identifierlctx.timestamp- Request timestamplctx.state- Request-scoped statelctx.lifecycle- Request lifecycle hooks
4. Routing
Define routes by mapping HTTP methods and paths to handlers:
routes: [
{ method: 'GET', path: '/users', handler: listUsersHandler },
{ method: 'GET', path: '/users/:id', handler: getUserHandler },
{ method: 'POST', path: '/users', handler: createUserHandler },
{ method: 'PUT', path: '/users/:id', handler: updateUserHandler },
{ method: 'DELETE', path: '/users/:id', handler: deleteUserHandler },
]Path Parameters:
- Use
:paramsyntax for dynamic segments - Access via
req.params.param
Query Parameters:
- Access via
req.query.param
Your First API Endpoint
Let's build a complete user API with CRUD operations:
Step 1: Create the Handler
Create src/handlers/user.ts:
import type { Handler } from 'gati';
import { HandlerError } from 'gati';
// Mock data
const users = [
{ id: '1', name: 'Alice', email: 'alice@example.com' },
{ id: '2', name: 'Bob', email: 'bob@example.com' },
];
// GET /users
export const listUsersHandler: Handler = (req, res) => {
const nameFilter = req.query.name as string | undefined;
let filtered = users;
if (nameFilter) {
filtered = users.filter(u =>
u.name.toLowerCase().includes(nameFilter.toLowerCase())
);
}
res.json({ users: filtered, count: filtered.length });
};
// GET /users/:id
export const getUserHandler: Handler = (req, res, gctx) => {
const userId = req.params.id;
// Use logger module
const logger = gctx.modules['logger'] as any;
logger?.log(`Fetching user ${userId}`);
const user = users.find(u => u.id === userId);
if (!user) {
throw new HandlerError('User not found', 404, { userId });
}
res.json({ user });
};
// POST /users
export const createUserHandler: Handler = (req, res) => {
const { name, email } = req.body as { name: string; email: string };
const newUser = {
id: String(users.length + 1),
name,
email,
};
users.push(newUser);
res.status(201).json({ user: newUser });
};Step 2: Add Routes
Update gati.config.ts:
import { helloHandler } from './src/handlers/hello';
import {
listUsersHandler,
getUserHandler,
createUserHandler
} from './src/handlers/user';
export default {
server: {
port: 3000,
host: 'localhost',
},
routes: [
{ method: 'GET', path: '/hello', handler: helloHandler },
{ method: 'GET', path: '/users', handler: listUsersHandler },
{ method: 'GET', path: '/users/:id', handler: getUserHandler },
{ method: 'POST', path: '/users', handler: createUserHandler },
],
modules: (gctx) => {
// Modules will be added later
},
};Step 3: Test Your Endpoints
# List all users
curl http://localhost:3000/users
# Get a specific user
curl http://localhost:3000/users/1
# Filter users by name
curl http://localhost:3000/users?name=Alice
# Create a new user
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"name":"Charlie","email":"charlie@example.com"}'Running Your Application
Development Mode
Start with hot-reload:
pnpm devChanges to your code will automatically reload the server.
Production Build
Build for production:
pnpm buildStart the production server:
pnpm startEnvironment Variables
Create a .env file:
NODE_ENV=development
PORT=3000
HOST=localhost
DATABASE_URL=postgresql://localhost/mydbAccess in your code:
const dbUrl = process.env.DATABASE_URL;Next Steps
Now that you have a basic Gati application running, explore these topics:
Learn Handler Development
- Handler Development Guide
- Path parameters and query strings
- Request body validation
- Error handling patterns
- Response formatting
Create Modules
- Module Creation Guide
- Module lifecycle
- Dependency injection
- Testing strategies
- Best practices
Understand Architecture
- Architecture Documentation
- Component relationships
- Request flow
- Design decisions
- Extension points
Advanced Topics
- Middleware: Add custom request processing
- Validation: Use Zod or similar for input validation
- Authentication: Implement auth middleware
- Database: Integrate PostgreSQL, MongoDB, etc.
- Testing: Write unit and integration tests
- Deployment: Deploy to Kubernetes, AWS, or GCP
Troubleshooting
Port Already in Use
Error: EADDRINUSE: address already in use
Solution: Change the port in gati.config.ts:
server: {
port: 8080, // Use a different port
}Or kill the process using the port:
# Find the process
lsof -i :3000
# Kill it
kill -9 <PID>TypeScript Errors
Error: Module not found or type errors
Solution: Ensure dependencies are installed:
pnpm installCheck your tsconfig.json paths configuration:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}Module Not Loaded
Error: Module undefined or null in handlers
Solution: Ensure modules are initialized in gati.config.ts:
modules: (gctx) => {
gctx.modules['logger'] = initLogger(gctx);
gctx.modules['db'] = initDatabase(gctx);
}Hot Reload Not Working
Error: Changes not reflected after saving
Solution:
- Restart the dev server
- Check for syntax errors in your code
- Ensure files are in the
src/directory
Request Body is Undefined
Error: req.body is undefined
Solution: Gati automatically parses JSON bodies. Ensure:
- Content-Type header is
application/json - Request body is valid JSON
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"name":"Alice","email":"alice@example.com"}'404 Not Found
Error: Route returns 404
Solution:
- Check route is registered in
gati.config.ts - Verify path matches exactly (case-sensitive)
- Ensure handler is imported correctly
import { myHandler } from './src/handlers/myHandler'; // Correct path
routes: [
{ method: 'GET', path: '/my-route', handler: myHandler },
]Additional Resources
- GitHub Repository: https://github.com/krishnapaul242/gati
- Example Projects: https://github.com/krishnapaul242/gati/tree/main/examples
- Issue Tracker: https://github.com/krishnapaul242/gati/issues
- Discussions: https://github.com/krishnapaul242/gati/discussions
Getting Help
- Community Discord: Join here (coming soon)
- Stack Overflow: Tag your questions with
gati-framework - GitHub Issues: For bug reports and feature requests
Next: Handler Development Guide →
See Also: