High-performance, zero-dependency TypeScript backend framework powered by uWebSockets.js
QWE adalah framework backend modern untuk Node.js yang mengutamakan performa tinggi, kemudahan penggunaan, dan arsitektur yang terstruktur. Dibangun di atas uWebSockets.js untuk kecepatan maksimal dengan API yang mirip NestJS namun tanpa decorator.
- π Ultra Fast: Powered by uWebSockets.js, mencapai 65K-95K req/s untuk operasi CRUD
- π― Zero Dependencies: Tidak ada dependency runtime, semua built-in menggunakan Node.js stdlib
- π¦ Modular Architecture: Struktur folder mirip NestJS (module/controller/service/router)
- π§ Dependency Injection: IoC container dengan auto-wiring berdasarkan parameter name
- π£οΈ Radix Router: Routing cepat dengan O(log n) complexity dan route caching
- β Zod-like API: Schema validation yang familiar dan type-safe
- π Chainable:
.string().min(2).max(100).email() - π Type Inference: Automatic TypeScript type inference dari schema
- π‘οΈ Validation Pipe: Middleware otomatis untuk request validation
- π Multi-Database: Support PostgreSQL, MySQL, SQLite, MSSQL, dan MongoDB
- π Prisma-like API:
where,orderBy,include,selectyang intuitif - β‘ Query Caching: LRU cache untuk query optimization
- π Prepared Statements: SQLite statement caching untuk performa maksimal
- ποΈ Migrations: Built-in migration system
- π JWT Authentication: Token-based auth dengan sign/verify/refresh
- π Password Hashing: Scrypt dengan salt dan timing-safe comparison
- π‘οΈ CORS: Configurable Cross-Origin Resource Sharing
- πͺ Helmet: Security headers otomatis
- β±οΈ Rate Limiting: Sliding window rate limiter
- π CSRF Protection: Double-submit cookie pattern
- ποΈ Compression: Brotli, Gzip, Deflate dengan auto-negotiation
- π€ File Upload: Streaming multipart parser
- πͺ Cookie Parser: Signed cookie support dengan HMAC
- π Logging: Structured logging dengan multiple transports
- β‘ Response Cache: LRU cache untuk response optimization
- π WebSocket: Real-time communication support
- π¨ Code Generator: Generate module/controller/service dengan 1 command
- π Postman Export: Auto-generate Postman collection dari routes
- ποΈ Migration Commands: Create, run, rollback migrations
Benchmark dilakukan dengan autocannon β 100 concurrent connections, 10s duration, pipelining 10.
Environment: AMD Ryzen 5 6600H, 13GB RAM, Linux 6.18, Node.js 22.
| Endpoint | QWE π | Express | Go | Rust |
|---|---|---|---|---|
| GET /users (list) | 84,921 | 7,014 | 61,309 | 100,576 |
| GET /users/1 (read) | 97,914 | 6,973 | 60,493 | 97,030 |
| POST /users (create) | 81,976 | 4,117 | 53,930 | 65,798 |
| PUT /users/1 (update) | 80,549 | 4,398 | 54,390 | 76,766 |
| DELETE /users/2 (delete) | 75,718 | 6,592 | 3,343 | 8,041 |
| Endpoint | QWE π | Express | Go | Rust |
|---|---|---|---|---|
| GET /users (list) | 11.30 | 141.26 | 15.96 | 9.50 |
| GET /users/1 (read) | 9.73 | 142.04 | 16.13 | 9.86 |
| POST /users (create) | 5.63 | 120.36 | 8.82 | 7.14 |
| PUT /users/1 (update) | 5.74 | 112.66 | 8.74 | 6.03 |
| DELETE /users/2 (delete) | 12.70 | 150.21 | 294.61 | 111.91 |
| Endpoint | QWE | Express | Go | Rust |
|---|---|---|---|---|
| GET /users (list) | 100% | 8% | 72% | 118% |
| GET /users/1 (read) | 100% | 7% | 62% | 99% |
| POST /users (create) | 100% | 5% | 66% | 80% |
| PUT /users/1 (update) | 100% | 5% | 68% | 95% |
| DELETE /users/2 (delete) | 100% | 9% | 4% | 11% |
- vs Express: QWE 14x lebih cepat (rata-rata 84K vs 5.8K req/s)
- vs Go: QWE 1.5x lebih cepat (rata-rata 84K vs 46K req/s)
- vs Rust: QWE bersaing ketat β menang di write operations (POST 1.2x, PUT 1.05x, DELETE 9.4x), Rust unggul tipis di GET list (+18%)
- P99 Latency: QWE konsisten di 14-38ms, jauh lebih rendah dari Express (165-232ms)
Stack: QWE (uWebSockets.js + better-sqlite3 WAL) | Express 4.x + better-sqlite3 | Go net/http + go-sqlite3 | Actix-web + rusqlite
# Buat project baru
mkdir my-api
cd my-api
npm init -y
# Install QWE
npm install qwe-framework
# Install TypeScript dan tools development
npm install -D typescript @types/node tsx# Initialize TypeScript
npx tsc --init
# Create project structure
mkdir -p src/{modules,common}// src/main.ts
import { Application } from 'qwe-framework';
const app = new Application();
// Simple route
app.get('/', (req, res) => {
res.json({ message: 'Hello, QWE! π' });
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});Run dengan:
npx tsx src/main.tssrc/
βββ main.ts # Entry point
βββ app.module.ts # Root module
βββ modules/
β βββ users/
β β βββ users.module.ts
β β βββ users.controller.ts
β β βββ users.service.ts
β β βββ users.router.ts
β β βββ dto/
β β βββ create-user.schema.ts
β βββ posts/
β βββ posts.module.ts
β βββ posts.controller.ts
β βββ posts.service.ts
β βββ posts.router.ts
βββ common/
β βββ guards/
β β βββ auth.guard.ts
β βββ middleware/
β β βββ logger.middleware.ts
β βββ filters/
β βββ http-exception.filter.ts
βββ database/
βββ connection.ts
βββ migrations/
βββ 001_create_users.ts
// src/modules/users/users.module.ts
import { Module } from 'qwe-framework';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { DatabaseService } from '../../database/connection';
@Module({
name: 'users',
prefix: '/users',
controllers: [UsersController],
providers: [
UsersService,
DatabaseService
]
})
export class UsersModule {}// src/modules/users/users.controller.ts
import { Controller, ExecutionContext } from 'qwe-framework';
import { UsersService } from './users.service';
import { v } from 'qwe-framework/validation';
@Controller()
export class UsersController {
constructor(private usersService: UsersService) {}
async getAll(ctx: ExecutionContext) {
const users = await this.usersService.findAll();
ctx.response.json({ data: users });
}
async getOne(ctx: ExecutionContext) {
const id = parseInt(ctx.request.params.id);
const user = await this.usersService.findById(id);
if (!user) {
ctx.response.status(404).json({ error: 'User not found' });
return;
}
ctx.response.json({ data: user });
}
async create(ctx: ExecutionContext) {
const user = await this.usersService.create(ctx.request.body);
ctx.response.status(201).json({ data: user });
}
async update(ctx: ExecutionContext) {
const id = parseInt(ctx.request.params.id);
const user = await this.usersService.update(id, ctx.request.body);
ctx.response.json({ data: user });
}
async delete(ctx: ExecutionContext) {
const id = parseInt(ctx.request.params.id);
await this.usersService.delete(id);
ctx.response.status(204).send();
}
}// src/modules/users/users.service.ts
import { Injectable } from 'qwe-framework';
import { DatabaseService } from '../../database/connection';
@Injectable()
export class UsersService {
constructor(private db: DatabaseService) {}
async findAll() {
return this.db.query('users')
.select(['id', 'name', 'email'])
.orderBy('created_at', 'desc')
.execute();
}
async findById(id: number) {
return this.db.query('users')
.where('id', '=', id)
.first();
}
async create(data: { name: string; email: string }) {
const result = await this.db.query('users')
.insert(data)
.returning('*')
.execute();
return result[0];
}
async update(id: number, data: Partial<{ name: string; email: string }>) {
const result = await this.db.query('users')
.where('id', '=', id)
.update(data)
.returning('*')
.execute();
return result[0];
}
async delete(id: number) {
await this.db.query('users')
.where('id', '=', id)
.delete()
.execute();
}
}// src/modules/users/users.router.ts
import { Router } from 'qwe-framework';
import { UsersController } from './users.controller';
import { AuthGuard } from '../../common/guards/auth.guard';
export const usersRouter = new Router();
// Public routes
usersRouter.get('/', UsersController, 'getAll');
usersRouter.get('/:id', UsersController, 'getOne');
// Protected routes (require authentication)
usersRouter.post('/', UsersController, 'create', {
guards: [AuthGuard]
});
usersRouter.put('/:id', UsersController, 'update', {
guards: [AuthGuard]
});
usersRouter.delete('/:id', UsersController, 'delete', {
guards: [AuthGuard]
});QWE menggunakan schema validation system yang mirip Zod:
import { v } from 'qwe-framework/validation';
// String validation
const nameSchema = v.string()
.min(2, 'Name must be at least 2 characters')
.max(100, 'Name cannot exceed 100 characters');
// Email validation
const emailSchema = v.string().email('Invalid email format');
// Number validation
const ageSchema = v.number()
.integer('Age must be an integer')
.min(0, 'Age cannot be negative')
.max(150, 'Invalid age');// src/modules/users/dto/create-user.schema.ts
import { v } from 'qwe-framework/validation';
import type { Infer } from 'qwe-framework/validation';
export const CreateUserSchema = v.object({
name: v.string().min(2).max(100),
email: v.string().email(),
age: v.number().integer().min(18).optional(),
role: v.enum(['user', 'admin']).default('user')
});
// Type inference
export type CreateUserDto = Infer<typeof CreateUserSchema>;import { ValidationPipe } from 'qwe-framework/validation';
import { CreateUserSchema } from './dto/create-user.schema';
// Method 1: Auto-validation with pipe
@UsePipe(ValidationPipe(CreateUserSchema))
async create(ctx: ExecutionContext) {
// ctx.request.body is already validated and typed
const user = await this.usersService.create(ctx.request.body);
ctx.response.status(201).json({ data: user });
}
// Method 2: Manual validation
async createManual(ctx: ExecutionContext) {
try {
const data = CreateUserSchema.parse(ctx.request.body);
const user = await this.usersService.create(data);
ctx.response.status(201).json({ data: user });
} catch (error) {
ctx.response.status(400).json({
error: 'Validation failed',
details: error.errors
});
}
}// src/database/connection.ts
import { DatabaseService } from 'qwe-framework/database';
export const db = new DatabaseService({
type: 'sqlite', // or 'postgres', 'mysql', 'mssql', 'mongodb'
filename: './database.sqlite',
// For PostgreSQL/MySQL:
// host: 'localhost',
// port: 5432,
// database: 'myapp',
// username: 'user',
// password: 'password'
});
// Enable WAL mode for SQLite (better performance)
db.raw('PRAGMA journal_mode = WAL');
db.raw('PRAGMA synchronous = NORMAL');// Simple select
const users = await db.query('users')
.select(['id', 'name', 'email'])
.where('active', '=', true)
.orderBy('created_at', 'desc')
.limit(10)
.execute();
// Complex where conditions
const filteredUsers = await db.query('users')
.where('age', '>', 18)
.where('status', '=', 'active')
.whereGroup((q) => {
q.where('role', '=', 'admin')
.orWhere('verified', '=', true);
})
.execute();
// Insert with returning
const newUser = await db.query('users')
.insert({ name: 'John', email: 'john@example.com' })
.returning('*')
.execute();
// Update
const updated = await db.query('users')
.where('id', '=', 1)
.update({ name: 'Jane' })
.returning('*')
.execute();
// Delete
await db.query('users')
.where('id', '=', 1)
.delete()
.execute();
// Count
const count = await db.query('users')
.where('active', '=', true)
.count();
// Join query
const usersWithPosts = await db.query('users')
.select(['users.*', 'posts.title'])
.join('posts', 'users.id', '=', 'posts.user_id')
.execute();// Simple transaction
await db.transaction(async (trx) => {
await trx.query('users')
.update({ balance: db.raw('balance - 100') })
.where('id', '=', 1)
.execute();
await trx.query('users')
.update({ balance: db.raw('balance + 100') })
.where('id', '=', 2)
.execute();
});
// Transaction with error handling
try {
await db.transaction(async (trx) => {
await trx.query('orders').insert(orderData).execute();
await trx.query('inventory')
.update({ quantity: db.raw('quantity - 1') })
.where('product_id', '=', productId)
.execute();
});
ctx.response.json({ success: true });
} catch (error) {
// Transaction automatically rolled back
ctx.response.status(500).json({ error: 'Transaction failed' });
}Create migration:
qwe migration create create_users_table// src/database/migrations/001_create_users.ts
import { Migration } from 'qwe-framework/database';
export default class CreateUsersTable extends Migration {
async up() {
await this.schema.createTable('users', (table) => {
table.increments('id').primary();
table.string('name', 100).notNullable();
table.string('email', 255).unique().notNullable();
table.string('password_hash', 255).notNullable();
table.enum('role', ['user', 'admin']).default('user');
table.boolean('active').default(true);
table.timestamp('created_at').default('now()');
table.timestamp('updated_at').default('now()');
table.index(['email']);
table.index(['created_at']);
});
}
async down() {
await this.schema.dropTable('users');
}
}Run migrations:
# Run all pending migrations
qwe migration run
# Rollback last migration
qwe migration rollback
# Reset all migrations
qwe migration reset
# Check migration status
qwe migration status// src/common/guards/auth.guard.ts
import { Guard, ExecutionContext } from 'qwe-framework';
import { JWT } from 'qwe-framework/security';
const jwtSecret = process.env.JWT_SECRET || 'your-secret-key';
export class AuthGuard implements Guard {
async canActivate(ctx: ExecutionContext): Promise<boolean> {
const authHeader = ctx.request.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
ctx.response.status(401).json({ error: 'No token provided' });
return false;
}
const token = authHeader.substring(7);
try {
const payload = JWT.verify(token, jwtSecret);
ctx.request.user = payload; // Attach user to request
return true;
} catch (error) {
ctx.response.status(401).json({ error: 'Invalid token' });
return false;
}
}
}import { Router } from 'qwe-framework';
import { AuthGuard } from '../common/guards/auth.guard';
const router = new Router();
// Protected route
router.get('/profile', ProfileController, 'getProfile', {
guards: [AuthGuard]
});// src/modules/auth/auth.controller.ts
import { v } from 'qwe-framework/validation';
import { JWT, hash } from 'qwe-framework/security';
const LoginSchema = v.object({
email: v.string().email(),
password: v.string().min(8)
});
@Controller()
export class AuthController {
constructor(private usersService: UsersService) {}
async login(ctx: ExecutionContext) {
const { email, password } = LoginSchema.parse(ctx.request.body);
const user = await this.usersService.findByEmail(email);
if (!user) {
ctx.response.status(401).json({ error: 'Invalid credentials' });
return;
}
const isValid = await hash.compare(password, user.passwordHash);
if (!isValid) {
ctx.response.status(401).json({ error: 'Invalid credentials' });
return;
}
// Generate JWT tokens
const accessToken = JWT.sign(
{ userId: user.id, email: user.email, role: user.role },
jwtSecret,
{ expiresIn: '15m' }
);
const refreshToken = JWT.sign(
{ userId: user.id },
jwtSecret,
{ expiresIn: '7d' }
);
ctx.response.json({
data: {
accessToken,
refreshToken,
user: {
id: user.id,
email: user.email,
role: user.role
}
}
});
}
}import { helmet } from 'qwe-framework/security';
// Enable helmet with default settings
app.use(helmet());
// Or with custom options
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"]
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true
}
}));import { cors } from 'qwe-framework/security';
// Allow all origins (development only)
app.use(cors());
// Production configuration
app.use(cors({
origin: ['https://example.com', 'https://app.example.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true,
maxAge: 86400 // 24 hours
}));import { rateLimit } from 'qwe-framework/security';
// Global rate limit
app.use(rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per window
}));
// Stricter limit for auth endpoints
router.post('/auth/login', AuthController, 'login', {
middleware: [rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 5 // 5 attempts per minute
})]
});import { compress } from 'qwe-framework/middleware';
// Enable compression with auto-negotiation
app.use(compress({
threshold: 1024, // Only compress responses > 1KB
level: 6 // Compression level (1-9)
}));import { Logger } from 'qwe-framework/logging';
// Create logger instance
const logger = new Logger({
name: 'app',
level: 'info', // 'debug', 'info', 'warn', 'error'
transport: 'console' // or 'file', 'json'
});
// Usage
logger.info('Server started', { port: 3000 });
logger.debug('Database query', { sql: 'SELECT * FROM users' });
logger.warn('High memory usage', { memoryUsage: '85%' });
logger.error('Failed to connect', { error: message });// src/modules/users/users.service.test.ts
import { test, describe } from 'qwe-framework/testing';
import { UsersService } from './users.service';
import { MockDatabase } from 'qwe-framework/testing/mocks';
describe('UsersService', () => {
test('should create user', async () => {
const mockDb = new MockDatabase();
const service = new UsersService(mockDb);
const result = await service.create({
name: 'John',
email: 'john@example.com'
});
expect(result).toBeDefined();
expect(result.name).toBe('John');
expect(mockDb.queries).toHaveLength(1);
});
});// tests/integration/users.test.ts
import { test, describe, createTestApp } from 'qwe-framework/testing';
import { UsersModule } from '../../src/modules/users/users.module';
describe('Users API', () => {
const app = createTestApp([UsersModule]);
test('GET /users should return list', async () => {
const response = await app.request('GET', '/users');
expect(response.status).toBe(200);
expect(response.json.data).toBeArray();
});
test('POST /users should create user', async () => {
const response = await app.request('POST', '/users', {
body: {
name: 'Jane',
email: 'jane@example.com'
},
headers: {
'Authorization': 'Bearer valid-token'
}
});
expect(response.status).toBe(201);
expect(response.json.data.name).toBe('Jane');
});
});# Create new project
qwe new my-project
# Generate module
qwe generate module users
# Generate controller
qwe generate controller users/users.controller
# Generate service
qwe generate service users/users.service
# Generate router
qwe generate router users/users.router
# Generate validation schema
qwe generate schema create-user
# Export Postman collection
qwe export postman --output ./api-collection.json
# Run migrations
qwe migration run
qwe migration rollback
qwe migration status
qwe migration reset
# Development server with hot reload
qwe dev
# Build for production
qwe build
# Start production server
qwe startconst app = new Application(options?: {
port?: number;
host?: string;
cors?: boolean | CorsOptions;
helmet?: boolean | HelmetOptions;
compress?: boolean | CompressOptions;
logger?: boolean | LoggerOptions;
});
// Methods
app.module(configure: (mod: ModuleBuilder) => void): this
app.use(middleware: Middleware): this
app.get(path: string, handler: Handler): this
app.post(path: string, handler: Handler): this
app.put(path: string, handler: Handler): this
app.patch(path: string, handler: Handler): this
app.delete(path: string, handler: Handler): this
app.listen(port?: number, host?: string): Promise<void>
app.close(): Promise<void>const router = new Router();
router.get(path: string, controller: Controller, method: string, options?: RouteOptions): this
router.post(path: string, controller: Controller, method: string, options?: RouteOptions): this
router.put(path: string, controller: Controller, method: string, options?: RouteOptions): this
router.patch(path: string, controller: Controller, method: string, options?: RouteOptions): this
router.delete(path: string, controller: Controller, method: string, options?: RouteOptions): this
// Route options
interface RouteOptions {
guards?: Guard[];
middleware?: Middleware[];
validation?: Schema;
}interface ExecutionContext {
request: QweRequest;
response: QweResponse;
params: Record<string, string>;
query: Record<string, string | string[]>;
}
interface QweRequest {
method: string;
url: string;
path: string;
headers: Record<string, string>;
body: any;
params: Record<string, string>;
query: Record<string, string | string[]>;
cookies: Record<string, string>;
ip: string;
user?: any; // Set by auth guard
}
interface QweResponse {
status(code: number): this;
json(data: any): void;
send(data: string | Buffer): void;
redirect(url: string, code?: number): void;
setHeader(name: string, value: string): this;
setCookie(name: string, value: string, options?: CookieOptions): this;
clearCookie(name: string): this;
}Contributions are welcome! Please see CONTRIBUTING.md for details.
MIT License - see LICENSE for details.
- GraphQL support
- WebSocket rooms and channels
- Built-in cache layer (Redis support)
- OpenAPI/Swagger documentation generator
- Hot module replacement for development
- Plugin system
- CLI interactive mode
- uWebSockets.js - High-performance HTTP server
- NestJS - Inspiration for architecture
- Zod - Inspiration for validation API
- Prisma - Inspiration for ORM API
Made with β€οΈ by the QWE Framework team
