uires careful architectural decisions regarding the execution pipeline, API design, and state management. Below is a technical breakdown of the core components.
1. Execution Pipeline
The runtime operates in two distinct phases:
- Bundling: The TypeScript source is processed by
esbuild. This step resolves imports, transpiles TypeScript to JavaScript, and produces a single optimized bundle. This eliminates runtime module resolution overhead.
- Execution: The bundle is loaded into a Go-hosted JavaScript engine. Go acts as the host, exposing specific functions to the JavaScript context. This allows the runtime to leverage Go's standard library for high-performance operations while maintaining a familiar TypeScript interface.
2. HTTP Server Architecture
The HTTP layer provides a router with Go-style path parameters and a context object for request/response handling. Unlike frameworks that rely on middleware chains with implicit state, Golt's server API is explicit and type-safe.
Implementation Example:
import { createServer } from 'runtime/http';
import { jsonResponse, textResponse } from 'runtime/context';
import { logger } from 'runtime/middleware';
const server = createServer({
port: 8080,
timeout: 30000,
});
server.use(logger({ level: 'info', format: 'json' }));
server.route('GET', '/health', (ctx) => {
return jsonResponse(ctx, {
status: 'ok',
runtime: 'golt',
uptime: process.uptime(),
});
});
server.route('GET', '/users/{userId}', (ctx) => {
const userId = ctx.param('userId');
const query = ctx.query('include_details');
if (!userId) {
return textResponse(ctx, 'Missing user ID', 400);
}
return jsonResponse(ctx, {
id: userId,
details: query === 'true' ? { role: 'admin' } : null,
});
});
server.route('NOT_FOUND', (ctx) => {
return jsonResponse(ctx, { error: 'Resource not found' }, 404);
});
server.listen();
Rationale:
- Go-Style Parameters: Using
{userId} syntax aligns with Go's chi or gin routers, providing a consistent mental model for developers familiar with Go web frameworks.
- Explicit Context: The
ctx object encapsulates request data and response helpers, preventing global state pollution.
- Middleware Integration: Middleware is applied explicitly, ensuring predictable execution order.
3. Database Access Pattern
Golt wraps Go's database/sql package, exposing a Promise-based API that enforces a strict separation between state-mutating commands and read operations. This design prevents common ORM pitfalls where accidental writes can occur during read queries.
Implementation Example:
import { openDatabase } from 'runtime/storage';
const db = openDatabase('sqlite', './production.db');
// Schema initialization
await db.execute(`
CREATE TABLE IF NOT EXISTS sessions (
token TEXT PRIMARY KEY,
user_id INTEGER NOT NULL,
expires_at INTEGER NOT NULL
)
`);
// Write operation: Use execute for INSERT, UPDATE, DELETE, DDL
await db.execute(
'INSERT INTO sessions (token, user_id, expires_at) VALUES (?, ?, ?)',
'tok_abc123',
42,
Date.now() + 86400000
);
// Read operation: Use select for queries returning rows
const activeSessions = await db.select(
'SELECT token, user_id FROM sessions WHERE expires_at > ?',
Date.now()
);
console.log(`Found ${activeSessions.length} active sessions.`);
Rationale:
execute vs. select: This distinction forces developers to be intentional about data mutation. execute returns metadata (rows affected), while select returns result sets. This reduces the risk of accidental data modification.
- Parameterized Queries: The API enforces parameter binding, mitigating SQL injection vulnerabilities.
- Go
database/sql Backend: Leveraging Go's battle-tested database driver ensures connection pooling, concurrency safety, and performance.
4. Security and Utility Primitives
The runtime includes built-in helpers for common backend tasks, eliminating the need for third-party libraries for critical security functions.
Implementation Example:
import { hashPassword, verifyHash } from 'runtime/security';
import { signToken, verifyToken } from 'runtime/auth';
import { env } from 'runtime/config';
import { readFile, writeFile } from 'runtime/fs';
// Password hashing
const plainPassword = 'user-secret';
const hashed = await hashPassword(plainPassword);
const isValid = await verifyHash(plainPassword, hashed);
// JWT management
const jwtSecret = env.get('JWT_SECRET_KEY');
if (!jwtSecret) throw new Error('JWT_SECRET_KEY is required');
const payload = { sub: 'user_42', role: 'editor' };
const token = signToken(payload, jwtSecret, 3600); // Expires in 1 hour
const decoded = verifyToken(token, jwtSecret);
console.log('Token valid:', decoded.sub);
// Filesystem operations
await writeFile('./logs/app.log', 'System initialized\n');
const content = await readFile('./logs/app.log');
console.log('Log content:', content);
Rationale:
- Explicit Imports: Utilities are imported from specific namespaces (
runtime/security, runtime/auth), making dependencies clear.
- Environment Variables:
env.get provides a type-safe way to access configuration, with runtime errors if required variables are missing.
- Synchronous/Async FS: Filesystem operations support both patterns, allowing flexibility based on the use case.
Pitfall Guide
Adopting a curated runtime requires a shift in development practices. Below are common mistakes and their resolutions.
| Pitfall | Explanation | Fix |
|---|
| Assuming Node Globals | Developers may attempt to use process.exit(), Buffer, or require(), which are not available in Golt. | Review the API surface documentation. Use runtime/fs for file ops and runtime/config for environment access. |
Misusing execute vs. select | Using execute for SELECT queries or select for INSERT statements can lead to errors or data loss. | Strictly use execute for mutations and select for reads. The runtime enforces this separation. |
| Ignoring Type Generation | Without proper type definitions, TypeScript loses its safety guarantees for runtime APIs. | Ensure the VS Code extension is installed and golt.json is present to trigger type generation in .golt/types. |
| Blocking the Go Host | Performing heavy CPU-bound tasks in JavaScript callbacks can block the Go event loop. | Offload CPU-intensive work to Go routines or use async patterns. Keep JS logic lightweight. |
| Hardcoding Secrets | Embedding JWT secrets or database credentials directly in source code. | Always use env.get() to load secrets from environment variables. Validate presence at startup. |
| Docker Volume Permissions | Mounting project directories into the Docker container may result in permission denied errors. | Run the container with the correct user ID or use --user flag. Ensure the workspace directory is writable. |
| Over-Engineering the TS Layer | Attempting to replicate complex Node.js patterns or libraries in TypeScript. | Embrace the minimalist philosophy. If logic is too complex, consider implementing it in Go and exposing it via the runtime. |
Production Bundle
This section provides actionable resources for deploying and maintaining Golt-based applications in production environments.
Action Checklist
Decision Matrix
Use this matrix to determine when Golt is the appropriate choice for your project.
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Microservice with strict security requirements | Golt | Minimal API surface reduces attack vector; Go backend ensures memory safety. | Lower security audit costs; reduced risk of vulnerabilities. |
| High-concurrency API with TypeScript team | Golt | Go's concurrency model handles high load efficiently; TS provides developer productivity. | Lower infrastructure costs due to efficient resource usage. |
| Legacy Node.js migration | Node.js/Deno | Golt is not a drop-in replacement; migration requires rewriting to use explicit APIs. | High migration effort; not recommended for legacy codebases. |
| Internal tooling / CLI scripts | Golt | Fast startup and single-binary deployment simplify distribution. | Reduced deployment complexity; faster iteration. |
| Heavy reliance on npm ecosystem | Node.js | Golt does not support npm packages; requires custom implementations. | High development cost to replicate npm functionality. |
Configuration Template
The golt.json file configures the runtime behavior and enables editor support.
{
"name": "my-backend-service",
"version": "1.0.0",
"entry": "src/main.ts",
"runtime": {
"port": 8080,
"timeout": 30000,
"logLevel": "info"
},
"database": {
"driver": "sqlite",
"path": "./data/app.db"
},
"security": {
"jwtSecretEnv": "JWT_SECRET_KEY",
"corsOrigins": ["https://myapp.com"]
}
}
Quick Start Guide
Follow these steps to initialize and run a Golt project in under five minutes.
-
Install Golt: Download the binary from the official release page or use the Docker image.
# Using Docker
docker pull aztekode/golt:latest
-
Initialize Project: Create a new project structure.
mkdir my-service && cd my-service
golt init
-
Write Code: Create app.ts with your server logic.
import { createServer } from 'runtime/http';
import { jsonResponse } from 'runtime/context';
const server = createServer({ port: 3000 });
server.route('GET', '/', (ctx) => {
return jsonResponse(ctx, { message: 'Hello from Golt' });
});
server.listen();
-
Run Application: Execute the script using the CLI or Docker.
# Using CLI
golt run app.ts
# Using Docker
docker run --rm -p 3000:3000 -v "$PWD":/workspace -w /workspace aztekode/golt:latest run app.ts
-
Verify: Access http://localhost:3000 to confirm the service is running.
Conclusion
Golt demonstrates the viability of curated runtimes for backend development. By combining TypeScript's developer experience with Go's performance and safety, it offers a compelling alternative for teams prioritizing determinism, security, and efficiency. The explicit API design enforces best practices, while the underlying Go architecture ensures robust execution. As the ecosystem matures, Golt is positioned to become a valuable tool for building reliable, high-performance backend services without the overhead of general-purpose runtimes.