moai-lang-javascript
A specialized JavaScript development assistant covering ES2024+ features, Node.js 22 LTS, Bun, Deno, and modern tooling like Vitest and ESLint 9. Provides ready-to-use code examples for backend frameworks (Express, Fastify, Hono), testing patterns, and deployment configurations including Docker.
Unlock Deep Analysis
Use AI to visualize the workflow and generate a realistic output preview for this skill.
Powered by Fastest LLM
Target Audience
JavaScript developers working on backend services, API development, or Node.js projects who need up-to-date syntax references and framework patterns.
Low security risk, safe to use
Quick Reference (30 seconds)
JavaScript ES2024+ Development Specialist - Modern JavaScript with Node.js 22 LTS, multiple runtimes, and contemporary tooling.
Auto-Triggers: .js, .mjs, .cjs files, package.json, Node.js projects, JavaScript discussions
Core Stack:
- ES2024+: Set methods, Promise.withResolvers, immutable arrays, import attributes
- Node.js 22 LTS: Native TypeScript, built-in WebSocket, stable watch mode
- Runtimes: Node.js 20/22 LTS, Deno 2.x, Bun 1.x
- Testing: Vitest, Jest, Node.js test runner
- Linting: ESLint 9 flat config, Biome
- Bundlers: Vite, esbuild, Rollup
- Frameworks: Express, Fastify, Hono, Koa
Quick Commands:
# Create Vite project
npm create vite@latest my-app -- --template vanilla
# Initialize with modern tooling
npm init -y && npm install -D vitest eslint @eslint/js
# Run with Node.js watch mode
node --watch server.js
# Run TypeScript directly in Node.js 22+
node --experimental-strip-types app.ts
Implementation Guide (5 minutes)
ES2024 Key Features
Set Operations:
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);
setA.intersection(setB); // Set {3, 4}
setA.union(setB); // Set {1, 2, 3, 4, 5, 6}
setA.difference(setB); // Set {1, 2}
setA.symmetricDifference(setB); // Set {1, 2, 5, 6}
setA.isSubsetOf(setB); // false
setA.isSupersetOf(setB); // false
setA.isDisjointFrom(setB); // false
Promise.withResolvers():
function createDeferred() {
const { promise, resolve, reject } = Promise.withResolvers();
return { promise, resolve, reject };
}
const deferred = createDeferred();
setTimeout(() => deferred.resolve('done'), 1000);
const result = await deferred.promise;
Immutable Array Methods:
const original = [3, 1, 4, 1, 5];
// New methods return new arrays (don't mutate)
const sorted = original.toSorted(); // [1, 1, 3, 4, 5]
const reversed = original.toReversed(); // [5, 1, 4, 1, 3]
const spliced = original.toSpliced(1, 2, 9); // [3, 9, 1, 5]
const changed = original.with(2, 99); // [3, 1, 99, 1, 5]
console.log(original); // [3, 1, 4, 1, 5] - unchanged
Object.groupBy and Map.groupBy:
const items = [
{ type: 'fruit', name: 'apple' },
{ type: 'vegetable', name: 'carrot' },
{ type: 'fruit', name: 'banana' },
];
const grouped = Object.groupBy(items, item => item.type);
// { fruit: [{...}, {...}], vegetable: [{...}] }
const mapGrouped = Map.groupBy(items, item => item.type);
// Map { 'fruit' => [...], 'vegetable' => [...] }
ES2025 Features
Import Attributes (JSON Modules):
import config from './config.json' with { type: 'json' };
import styles from './styles.css' with { type: 'css' };
console.log(config.apiUrl);
RegExp.escape:
const userInput = 'hello (world)';
const safePattern = RegExp.escape(userInput);
// "hello\\ \\(world\\)"
const regex = new RegExp(safePattern);
Node.js 22 LTS Features
Built-in WebSocket Client:
const ws = new WebSocket('wss://example.com/socket');
ws.addEventListener('open', () => {
ws.send(JSON.stringify({ type: 'hello' }));
});
ws.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
console.log('Received:', data);
});
Native TypeScript Support (Experimental):
# Run .ts files directly in Node.js 22.6+
node --experimental-strip-types app.ts
# In Node.js 22.18+, type stripping is enabled by default
node app.ts
Watch Mode (Stable):
# Auto-restart on file changes
node --watch server.js
# Watch specific files
node --watch-path=./src --watch-path=./config server.js
Permission Model:
# Restrict file system access
node --permission --allow-fs-read=/app/data server.js
# Restrict network access
node --permission --allow-net=api.example.com server.js
Backend Frameworks
Express (Traditional):
import express from 'express';
const app = express();
app.use(express.json());
app.get('/api/users', async (req, res) => {
const users = await db.users.findAll();
res.json(users);
});
app.post('/api/users', async (req, res) => {
const user = await db.users.create(req.body);
res.status(201).json(user);
});
app.listen(3000, () => console.log('Server running on port 3000'));
Fastify (High Performance):
import Fastify from 'fastify';
const fastify = Fastify({ logger: true });
const userSchema = {
body: {
type: 'object',
required: ['name', 'email'],
properties: {
name: { type: 'string', minLength: 2 },
email: { type: 'string', format: 'email' },
},
},
};
fastify.post('/api/users', { schema: userSchema }, async (request, reply) => {
const user = await db.users.create(request.body);
return reply.code(201).send(user);
});
await fastify.listen({ port: 3000 });
Hono (Edge-First):
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
import { validator } from 'hono/validator';
const app = new Hono();
app.use('*', logger());
app.use('/api/*', cors());
app.get('/api/users', async (c) => {
const users = await db.users.findAll();
return c.json(users);
});
app.post('/api/users',
validator('json', (value, c) => {
if (!value.name || !value.email) {
return c.json({ error: 'Invalid input' }, 400);
}
return value;
}),
async (c) => {
const user = await db.users.create(c.req.valid('json'));
return c.json(user, 201);
}
);
export default app;
Testing with Vitest
Configuration:
// vitest.config.js
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'node',
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
},
},
});
Test Example:
// user.test.js
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { createUser, getUser } from './user.js';
describe('User Service', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('should create a user', async () => {
const user = await createUser({ name: 'John', email: 'john@example.com' });
expect(user).toMatchObject({ name: 'John', email: 'john@example.com' });
expect(user.id).toBeDefined();
});
it('should throw on invalid email', async () => {
await expect(createUser({ name: 'John', email: 'invalid' }))
.rejects.toThrow('Invalid email');
});
});
ESLint 9 Flat Config
// eslint.config.js
import js from '@eslint/js';
import globals from 'globals';
export default [
js.configs.recommended,
{
languageOptions: {
ecmaVersion: 2025,
sourceType: 'module',
globals: {
...globals.node,
...globals.es2025,
},
},
rules: {
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'no-console': ['warn', { allow: ['warn', 'error'] }],
'prefer-const': 'error',
'no-var': 'error',
},
},
];
Biome (All-in-One)
{
"$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
"organizeImports": { "enabled": true },
"linter": {
"enabled": true,
"rules": { "recommended": true }
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"semicolons": "always"
}
}
}
Advanced Patterns
For comprehensive documentation including advanced async patterns, module system details, performance optimization, and production deployment configurations, see:
- reference.md - Complete API reference, Context7 library mappings, package manager comparison
- examples.md - Production-ready code examples, full-stack patterns, testing templates
Context7 Integration
// Node.js - mcp__context7__get_library_docs("/nodejs/node", "esm modules async", 1)
// Express - mcp__context7__get_library_docs("/expressjs/express", "middleware routing", 1)
// Fastify - mcp__context7__get_library_docs("/fastify/fastify", "plugins hooks", 1)
// Hono - mcp__context7__get_library_docs("/honojs/hono", "middleware validators", 1)
// Vitest - mcp__context7__get_library_docs("/vitest-dev/vitest", "mocking coverage", 1)
Works Well With
moai-lang-typescript- TypeScript integration, type checking with JSDocmoai-domain-backend- API design, microservices architecturemoai-domain-database- Database integration, ORM patternsmoai-workflow-testing- TDD workflows, testing strategiesmoai-foundation-quality- Code quality standardsmoai-essentials-debug- Debugging JavaScript applications
Quick Troubleshooting
Module System Issues:
# Check package.json type
cat package.json | grep '"type"'
# ESM: "type": "module" - use import/export
# CommonJS: "type": "commonjs" or omitted - use require/module.exports
Node.js Version Check:
node --version # Should be 20.x or 22.x LTS
npm --version # Should be 10.x+
Common Fixes:
# Clear npm cache
npm cache clean --force
# Delete node_modules and reinstall
rm -rf node_modules package-lock.json && npm install
# Fix permission issues
npm config set prefix ~/.npm-global
ESM/CommonJS Interop:
// Import CommonJS from ESM
import pkg from 'commonjs-package';
const { namedExport } = pkg;
// Dynamic import in CommonJS
const { default: esmModule } = await import('esm-package');
Last Updated: 2026-01-05 Status: Active (v1.1.0)
Referenced Files
The following files are referenced in this skill and included for context.
reference.md
# JavaScript Development Reference
## ES2024/ES2025 Complete Reference
### ES2024 Feature Matrix
| Feature | Description | Use Case |
|---------|-------------|----------|
| Set Methods | intersection, union, difference, etc. | Collection operations |
| Promise.withResolvers | External resolve/reject access | Deferred promises |
| Immutable Arrays | toSorted, toReversed, toSpliced, with | Functional programming |
| Object.groupBy | Group array items by key | Data categorization |
| Unicode String Methods | isWellFormed, toWellFormed | Unicode validation |
| ArrayBuffer Resizing | resize, transfer methods | Memory management |
### ES2025 Feature Matrix
| Feature | Description | Use Case |
|---------|-------------|----------|
| Import Attributes | with { type: 'json' } | JSON/CSS modules |
| RegExp.escape | Escape regex special chars | Safe regex patterns |
| Iterator Helpers | map, filter, take on iterators | Lazy iteration |
| Float16Array | 16-bit floating point arrays | ML/Graphics |
| Duplicate Named Capture Groups | Same name in regex alternation | Pattern matching |
### Complete Set Operations
```javascript
const setA = new Set([1, 2, 3, 4, 5]);
const setB = new Set([4, 5, 6, 7, 8]);
// Union - all elements from both sets
const union = setA.union(setB);
// Set {1, 2, 3, 4, 5, 6, 7, 8}
// Intersection - elements in both sets
const intersection = setA.intersection(setB);
// Set {4, 5}
// Difference - elements in A but not in B
const difference = setA.difference(setB);
// Set {1, 2, 3}
// Symmetric Difference - elements in either but not both
const symmetricDiff = setA.symmetricDifference(setB);
// Set {1, 2, 3, 6, 7, 8}
// Subset check - all elements of A are in B
setA.isSubsetOf(setB); // false
new Set([4, 5]).isSubsetOf(setB); // true
// Superset check - A contains all elements of B
setA.isSupersetOf(new Set([1, 2])); // true
// Disjoint check - no common elements
setA.isDisjointFrom(new Set([10, 11])); // true
Iterator Helpers (ES2025)
function* fibonacci() {
let a = 0, b = 1;
while (true) {
yield a;
[a, b] = [b, a + b];
}
}
// Take first 10 Fibonacci numbers
const first10 = fibonacci().take(10).toArray();
// [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
// Filter and map
const evenFib = fibonacci()
.filter(n => n % 2 === 0)
.map(n => n * 2)
.take(5)
.toArray();
// [0, 4, 16, 68, 288]
// Reduce with iterator
const sum = fibonacci()
.take(10)
.reduce((acc, n) => acc + n, 0);
// 88
// forEach on iterator
fibonacci()
.take(5)
.forEach(n => console.log(n));
// Find on iterator
const firstOver100 = fibonacci().find(n => n > 100);
// 144
// Some and every
fibonacci().take(10).some(n => n > 10); // true
fibonacci().take(5).every(n => n < 10); // true
Node.js Runtime Reference
Node.js Version Comparison
| Feature | Node.js 20 LTS | Node.js 22 LTS |
|---|---|---|
| ES Modules | Full support | Full support |
| Fetch API | Stable | Stable |
| WebSocket | Experimental | Stable (default) |
| Watch Mode | Experimental | Stable |
| TypeScript | Via loaders | Native (strip types) |
| Permission Model | Experimental | Stable |
| Test Runner | Stable | Enhanced |
| Startup Time | Baseline | 30% faster |
Node.js Built-in Test Runner
// test/user.test.js
import { test, describe, before, after, mock } from 'node:test';
import assert from 'node:assert';
import { createUser, getUser } from '../src/user.js';
describe('User Service', () => {
let mockDb;
before(() => {
mockDb = mock.fn(() => ({ id: 1, name: 'Test' }));
});
after(() => {
mock.reset();
});
test('creates user successfully', async (t) => {
const user = await createUser({ name: 'John', email: 'john@test.com' });
assert.ok(user.id);
assert.strictEqual(user.name, 'John');
});
test('throws on duplicate email', async (t) => {
await assert.rejects(
async () => createUser({ name: 'Jane', email: 'existing@test.com' }),
{ code: 'DUPLICATE_EMAIL' }
);
});
test('skipped test', { skip: true }, () => {
// This test will be skipped
});
test('todo test', { todo: 'implement later' }, () => {
// This test is marked as todo
});
});
Run tests:
# Run all tests
node --test
# Run specific file
node --test test/user.test.js
# With coverage
node --test --experimental-test-coverage
# Watch mode
node --test --watch
# Parallel execution
node --test --test-concurrency=4
Module System Deep Dive
Package.json Configuration:
{
"name": "my-package",
"version": "1.0.0",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
},
"./utils": {
"import": "./dist/utils.js",
"require": "./dist/utils.cjs"
}
},
"engines": {
"node": ">=20.0.0"
}
}
ESM/CommonJS Interoperability:
// ESM importing CommonJS
import cjsModule from 'commonjs-package';
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const cjsPackage = require('commonjs-only-package');
// Get __dirname and __filename in ESM
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Dynamic import (works in both)
const module = await import('./dynamic-module.js');
Package Manager Comparison
| Feature | npm | yarn | pnpm | bun |
|---|---|---|---|---|
| Speed | Baseline | Faster | Fastest Node | Fastest overall |
| Disk Usage | High | High | Low (symlinks) | Low |
| Workspaces | Yes | Yes | Yes | Yes |
| Lockfile | package-lock.json | yarn.lock | pnpm-lock.yaml | bun.lockb |
| Plug'n'Play | No | Yes | No | No |
| Node.js Only | Yes | Yes | Yes | No (own runtime) |
pnpm Commands
# Initialize
pnpm init
# Install dependencies
pnpm install
pnpm add express
pnpm add -D vitest
# Workspaces
pnpm -r install # Install all workspaces
pnpm --filter=api test # Run in specific workspace
# Performance
pnpm store prune # Clean unused packages
pnpm dedupe # Deduplicate dependencies
Bun Commands
# Initialize
bun init
# Install (30x faster than npm)
bun install
bun add express
bun add -d vitest
# Run scripts
bun run dev
bun run test
# Execute files directly (native TypeScript/JSX support)
bun run server.js
bun run app.ts
bun run app.tsx
# Built-in bundler
bun build ./src/index.ts --outdir=./dist
bun build ./index.html --outdir=./dist # HTML bundling
bun build --splitting # Code splitting
bun build --minify # Minification
# Compile to standalone executable
bun build --compile ./src/index.ts --outfile=myapp
# Run tests
bun test
bun test --watch
bun test --coverage
bun test --bail # Stop on first failure
# Hot reloading (state preserved)
bun --hot server.ts
Bun Complete API Reference
Bun.serve() - HTTP Server
Basic Server:
const server = Bun.serve({
port: 3000,
hostname: "0.0.0.0",
fetch(req) {
const url = new URL(req.url);
if (url.pathname === "/") return new Response("Hello!");
if (url.pathname === "/json") {
return Response.json({ message: "Hello JSON" });
}
return new Response("Not Found", { status: 404 });
},
error(error) {
return new Response(`Error: ${error.message}`, { status: 500 });
},
});
console.log(`Server running at ${server.url}`);
Routes Configuration (Bun 1.2.3+):
Bun.serve({
routes: {
// Static routes
"/api/status": new Response("OK"),
// Dynamic routes with parameters
"/users/:id": req => new Response(`User ${req.params.id}`),
// Per-method handlers
"/api/posts": {
GET: () => Response.json({ posts: [] }),
POST: async req => {
const body = await req.json();
return Response.json({ created: true, ...body }, { status: 201 });
},
},
// Wildcard routes
"/api/*": Response.json({ error: "Not found" }, { status: 404 }),
// Static file serving
"/favicon.ico": Bun.file("./favicon.ico"),
},
// Fallback handler
fetch(req) {
return new Response("Not Found", { status: 404 });
},
});
TLS/HTTPS Configuration:
Bun.serve({
port: 443,
tls: {
cert: Bun.file("./cert.pem"),
key: Bun.file("./key.pem"),
ca: Bun.file("./ca.pem"), // Optional CA certificate
passphrase: "secret", // Optional key passphrase
},
fetch(req) {
return new Response("Secure!");
},
});
WebSocket Server with Full Options:
Bun.serve({
port: 3001,
fetch(req, server) {
const success = server.upgrade(req, {
data: { userId: crypto.randomUUID() }, // Per-connection data
});
if (success) return undefined;
return new Response("Upgrade failed", { status: 400 });
},
websocket: {
open(ws) {
ws.subscribe("chat"); // Subscribe to topic
console.log("Connected:", ws.data.userId);
},
message(ws, message) {
ws.publish("chat", message); // Publish to all subscribers
ws.send(`Echo: ${message}`);
},
close(ws, code, reason) {
ws.unsubscribe("chat");
console.log("Disconnected:", code, reason);
},
// Advanced options
maxPayloadLength: 16 * 1024 * 1024, // 16MB max message size
backpressureLimit: 1024 * 1024, // 1MB backpressure limit
idleTimeout: 120, // 2 minutes idle timeout
perMessageDeflate: true, // Enable compression
sendPings: true, // Send ping frames
},
});
Server Metrics and Lifecycle:
const server = Bun.serve({
fetch(req, server) {
// Get client IP
const ip = server.requestIP(req);
// Set custom timeout for this request
server.timeout(req, 60); // 60 seconds
return Response.json({
activeRequests: server.pendingRequests,
activeWebSockets: server.pendingWebSockets,
chatUsers: server.subscriberCount("chat"),
clientIP: ip?.address,
});
},
});
// Hot reload routes without restart
server.reload({
routes: {
"/api/version": () => Response.json({ version: "2.0.0" }),
},
});
// Graceful shutdown
await server.stop(); // Wait for in-flight requests
await server.stop(true); // Force close all connections
Bun.file() - File Operations
Reading Files:
const file = Bun.file("./data.txt");
// File metadata
file.size; // number of bytes
file.type; // MIME type
await file.exists(); // boolean
// Reading methods
const text = await file.text(); // string
const json = await file.json(); // parsed JSON
const bytes = await file.bytes(); // Uint8Array
const buffer = await file.arrayBuffer(); // ArrayBuffer
const stream = file.stream(); // ReadableStream
// Partial reads (HTTP Range header)
const first1KB = await file.slice(0, 1024).text();
const last500 = await file.slice(-500).text();
// File references
Bun.file(1234); // file descriptor
Bun.file(new URL(import.meta.url)); // file:// URL
Bun.file("data.json", { type: "application/json" }); // custom MIME
Writing Files:
// Simple writes (returns bytes written)
await Bun.write("./output.txt", "Hello, Bun!");
await Bun.write("./data.json", JSON.stringify({ key: "value" }));
// Copy file
await Bun.write(Bun.file("output.txt"), Bun.file("input.txt"));
// Write from Response
const response = await fetch("https://example.com");
await Bun.write("index.html", response);
// Write to stdout
await Bun.write(Bun.stdout, "Hello stdout!\n");
// Delete file
await Bun.file("temp.txt").delete();
Incremental Writing (FileSink):
const file = Bun.file("large-output.txt");
const writer = file.writer({ highWaterMark: 1024 * 1024 }); // 1MB buffer
writer.write("First chunk\n");
writer.write("Second chunk\n");
writer.flush(); // Flush buffer to disk
writer.end(); // Flush and close
// Control process lifecycle
writer.unref(); // Allow process to exit
writer.ref(); // Re-ref later
Bun Shell
Basic Usage:
import { $ } from "bun";
// Execute commands
await $`echo "Hello World!"`;
// Get output
const text = await $`ls -la`.text();
const json = await $`cat config.json`.json();
const blob = await $`cat image.png`.blob();
// Iterate lines
for await (const line of $`cat file.txt`.lines()) {
console.log(line);
}
Piping and Redirection:
// Pipe commands
const wordCount = await $`echo "Hello World!" | wc -w`.text();
// Redirect to file
await $`echo "Hello" > greeting.txt`;
await $`echo "More" >> greeting.txt`; // Append
// Redirect stderr
await $`command 2> error.log`;
await $`command &> all.log`; // Both stdout and stderr
// JavaScript object as stdin
const response = new Response("input data");
await $`cat < ${response} | wc -c`;
Environment and Working Directory:
// Inline environment
await $`FOO=bar bun -e 'console.log(process.env.FOO)'`;
// Per-command environment
await $`echo $API_KEY`.env({ ...process.env, API_KEY: "secret" });
// Global environment
$.env({ NODE_ENV: "production" });
// Working directory
await $`pwd`.cwd("/tmp");
$.cwd("/home/user");
Error Handling:
// Default: throws on non-zero exit code
try {
await $`failing-command`.text();
} catch (err) {
console.log(`Exit code: ${err.exitCode}`);
console.log(err.stdout.toString());
console.log(err.stderr.toString());
}
// Disable throwing
const { stdout, stderr, exitCode } = await $`command`.nothrow().quiet();
// Global configuration
$.nothrow(); // Don't throw by default
$.throws(true); // Restore default
Bun SQLite
Database Connection:
import { Database } from "bun:sqlite";
// File-based
const db = new Database("mydb.sqlite");
// In-memory
const memoryDb = new Database(":memory:");
// Options
const db = new Database("mydb.sqlite", {
readonly: true, // Read-only mode
create: true, // Create if not exists
strict: true, // Enable strict mode
safeIntegers: true, // Return bigint for large integers
});
// Import via attribute
import db from "./mydb.sqlite" with { type: "sqlite" };
// WAL mode (recommended for performance)
db.run("PRAGMA journal_mode = WAL;");
Prepared Statements:
// Create table
db.run(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE
)
`);
// Prepare and execute
const insertUser = db.prepare(
"INSERT INTO users (name, email) VALUES ($name, $email)"
);
insertUser.run({ $name: "John", $email: "john@example.com" });
// Query methods
const getUser = db.prepare("SELECT * FROM users WHERE id = ?");
const user = getUser.get(1); // Single row as object
const users = getUser.all(); // All rows as array
const values = getUser.values(); // All rows as arrays
const result = getUser.run(); // { lastInsertRowid, changes }
// Iterate lazily
for (const row of getUser.iterate()) {
console.log(row);
}
// Map to class
class User {
id: number;
name: string;
get displayName() { return `User: ${this.name}`; }
}
const users = db.query("SELECT * FROM users").as(User).all();
Transactions:
const insertUser = db.prepare("INSERT INTO users (name) VALUES (?)");
// Transaction function
const insertMany = db.transaction((names: string[]) => {
for (const name of names) {
insertUser.run(name);
}
return names.length;
});
const count = insertMany(["Alice", "Bob", "Charlie"]);
// Transaction types
insertMany(data); // BEGIN
insertMany.deferred(data); // BEGIN DEFERRED
insertMany.immediate(data); // BEGIN IMMEDIATE
insertMany.exclusive(data); // BEGIN EXCLUSIVE
Bun Test
Test Structure:
import { describe, it, test, expect, beforeAll, beforeEach, afterEach, afterAll, mock } from "bun:test";
describe("User Service", () => {
beforeAll(() => { /* Setup once */ });
beforeEach(() => { /* Setup each */ });
afterEach(() => { /* Cleanup each */ });
afterAll(() => { /* Cleanup once */ });
it("should create a user", () => {
expect({ name: "John" }).toEqual({ name: "John" });
});
test("async operations", async () => {
const data = await fetchData();
expect(data).toBeDefined();
});
// Concurrent tests
test.concurrent("parallel test 1", async () => { /* ... */ });
test.concurrent("parallel test 2", async () => { /* ... */ });
// Skip and todo
test.skip("skipped test", () => { /* ... */ });
test.todo("implement later");
});
Mocking:
import { mock, spyOn } from "bun:test";
// Mock function
const fn = mock(() => 42);
fn();
expect(fn).toHaveBeenCalled();
expect(fn).toHaveBeenCalledTimes(1);
// Spy on existing
const spy = spyOn(console, "log");
console.log("test");
expect(spy).toHaveBeenCalledWith("test");
Snapshots:
test("snapshot", () => {
expect({ a: 1, b: 2 }).toMatchSnapshot();
});
// Update: bun test --update-snapshots
Test Commands:
bun test # Run all tests
bun test --watch # Watch mode
bun test --coverage # Code coverage
bun test --bail # Stop on first failure
bun test --timeout 10000 # 10s timeout
bun test -t "pattern" # Filter by name
bun test --concurrent # Parallel execution
bun test --reporter=junit # JUnit XML output
Bun S3 Client
Basic Operations:
import { s3, S3Client } from "bun";
// Read from S3
const file = s3.file("data/config.json");
const data = await file.json();
const text = await file.text();
const bytes = await file.bytes();
const stream = file.stream();
// Partial read
const first1KB = await file.slice(0, 1024).text();
// Write to S3
await file.write("Hello World!");
await file.write(JSON.stringify(data), { type: "application/json" });
// Delete
await file.delete();
// Check existence
const exists = await file.exists();
const size = await file.size();
Presigned URLs:
// Download URL
const downloadUrl = s3.presign("my-file.txt", {
expiresIn: 3600, // 1 hour
});
// Upload URL
const uploadUrl = s3.presign("my-file.txt", {
method: "PUT",
expiresIn: 3600,
type: "application/json",
acl: "public-read",
});
S3 Client Configuration:
const client = new S3Client({
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
bucket: "my-bucket",
region: "us-east-1",
// Or use endpoint for S3-compatible services
endpoint: "https://s3.us-east-1.amazonaws.com",
});
// Works with: AWS S3, Cloudflare R2, DigitalOcean Spaces,
// MinIO, Google Cloud Storage, Supabase Storage
Large File Streaming:
const writer = s3.file("large-file.bin").writer({
retry: 3,
queueSize: 10,
partSize: 5 * 1024 * 1024, // 5MB chunks
});
for (const chunk of largeData) {
writer.write(chunk);
await writer.flush();
}
await writer.end();
Bun Glob
Pattern Matching:
import { Glob } from "bun";
// Create glob instance
const glob = new Glob("**/*.ts");
// Async iteration
for await (const file of glob.scan(".")) {
console.log(file);
}
// Sync iteration
for (const file of glob.scanSync(".")) {
console.log(file);
}
// String matching
glob.match("src/index.ts"); // true
glob.match("src/index.js"); // false
Scan Options:
const glob = new Glob("**/*.ts");
for await (const file of glob.scan({
cwd: "./src",
dot: true, // Include dotfiles
absolute: true, // Return absolute paths
followSymlinks: true, // Follow symlinks
onlyFiles: true, // Files only (default)
})) {
console.log(file);
}
Pattern Syntax:
// Single character: ?
new Glob("???.ts").match("foo.ts"); // true
// Zero or more chars (no path sep): *
new Glob("*.ts").match("index.ts"); // true
new Glob("*.ts").match("src/index.ts"); // false
// Any chars including path sep: **
new Glob("**/*.ts").match("src/index.ts"); // true
// Character sets: [abc], [a-z], [^abc]
new Glob("ba[rz].ts").match("bar.ts"); // true
new Glob("ba[!a-z].ts").match("ba1.ts"); // true
// Alternation: {a,b,c}
new Glob("{src,lib}/**/*.ts").match("src/index.ts"); // true
// Negation: !
new Glob("!node_modules/**").match("src/index.ts"); // true
Bun Semver
Version Comparison:
import { semver } from "bun";
// Check if version satisfies range
semver.satisfies("1.0.0", "^1.0.0"); // true
semver.satisfies("2.0.0", "^1.0.0"); // false
semver.satisfies("1.0.0", "~1.0.0"); // true
semver.satisfies("1.0.0", "1.0.x"); // true
semver.satisfies("1.0.0", "1.0.0 - 2.0.0"); // true
// Compare versions
semver.order("1.0.0", "1.0.0"); // 0
semver.order("1.0.0", "1.0.1"); // -1
semver.order("1.0.1", "1.0.0"); // 1
// Sort versions
const versions = ["1.0.0", "1.0.1", "1.0.0-alpha", "1.0.0-beta"];
versions.sort(semver.order);
// ["1.0.0-alpha", "1.0.0-beta", "1.0.0", "1.0.1"]
Bun DNS
DNS Resolution:
import { dns } from "bun";
import * as nodeDns from "node:dns";
// Prefetch DNS (optimization)
dns.prefetch("api.example.com", 443);
// Get cache stats
const stats = dns.getCacheStats();
console.log(stats);
// { cacheHitsCompleted, cacheHitsInflight, cacheMisses, size, errors, totalCount }
// Node.js compatible API
const addrs = await nodeDns.promises.resolve4("bun.sh", { ttl: true });
// [{ address: "172.67.161.226", family: 4, ttl: 0 }, ...]
Bun Bundler
Build API:
const result = await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
target: "browser", // browser, bun, node
format: "esm", // esm, cjs, iife
splitting: true, // Code splitting
minify: true, // Full minification
sourcemap: "linked", // none, linked, external, inline
// Environment variables
env: "PUBLIC_*", // Inline PUBLIC_* vars
// External modules
external: ["lodash"],
// Custom loaders
loader: {
".png": "dataurl",
".txt": "file",
},
// Naming patterns
naming: {
entry: "[dir]/[name].[ext]",
chunk: "[name]-[hash].[ext]",
asset: "[name]-[hash].[ext]",
},
// Public path for CDN
publicPath: "https://cdn.example.com/",
// Define replacements
define: {
"process.env.VERSION": JSON.stringify("1.0.0"),
},
// Drop function calls
drop: ["console", "debugger"],
// Plugins
plugins: [myPlugin],
});
if (!result.success) {
console.error(result.logs);
}
HTML Bundling:
// Build static site
await Bun.build({
entrypoints: ["./index.html", "./about.html"],
outdir: "./dist",
minify: true,
});
// Or run dev server
// bun ./index.html
Bun Plugins
Plugin Structure:
import type { BunPlugin } from "bun";
const myPlugin: BunPlugin = {
name: "my-plugin",
setup(build) {
// Runs when bundle starts
build.onStart(() => {
console.log("Bundle started!");
});
// Custom module resolution
build.onResolve({ filter: /^virtual:/ }, args => {
return { path: args.path, namespace: "virtual" };
});
// Custom module loading
build.onLoad({ filter: /.*/, namespace: "virtual" }, args => {
return {
contents: `export default "Virtual module: ${args.path}"`,
loader: "js",
};
});
},
};
await Bun.build({
entrypoints: ["./index.ts"],
plugins: [myPlugin],
});
Bun Macros
Bundle-Time Execution:
// getVersion.ts
export function getVersion() {
const { stdout } = Bun.spawnSync({
cmd: ["git", "rev-parse", "HEAD"],
stdout: "pipe",
});
return stdout.toString().trim();
}
// app.ts
import { getVersion } from "./getVersion.ts" with { type: "macro" };
// Executed at bundle-time, result inlined
console.log(`Version: ${getVersion()}`);
Fetch at Bundle-Time:
// fetchData.ts
export async function fetchConfig(url: string) {
const response = await fetch(url);
return response.json();
}
// app.ts
import { fetchConfig } from "./fetchData.ts" with { type: "macro" };
// Config fetched during build, not runtime
const config = fetchConfig("https://api.example.com/config");
Bun Hot Reloading
State-Preserving Reload:
// server.ts
declare global {
var count: number;
}
globalThis.count ??= 0;
globalThis.count++;
Bun.serve({
fetch(req) {
return new Response(`Reloaded ${globalThis.count} times`);
},
port: 3000,
});
// Run with: bun --hot server.ts
// Changes reload without restarting process
// Global state (globalThis) preserved
Bun Workers
Web Workers:
// worker.ts
declare var self: Worker;
self.onmessage = (event: MessageEvent) => {
const result = heavyComputation(event.data);
self.postMessage(result);
};
function heavyComputation(input: number): number {
return input * 2;
}
// main.ts
const worker = new Worker(new URL("./worker.ts", import.meta.url));
worker.onmessage = (event) => {
console.log("Result:", event.data);
};
worker.postMessage(42);
Bun Utilities
Password Hashing:
const hash = await Bun.password.hash("mypassword", {
algorithm: "argon2id", // argon2id, argon2i, argon2d, bcrypt
memoryCost: 65536,
timeCost: 3,
});
const isValid = await Bun.password.verify("mypassword", hash);
Other Utilities:
// Sleep
await Bun.sleep(1000); // 1 second
// UUID v7 (time-ordered)
const uuid = Bun.randomUUIDv7();
// Environment variables (auto-loads .env)
const apiKey = Bun.env.API_KEY;
// Peek at promises without awaiting
const status = Bun.peek(promise); // pending, fulfilled, rejected
// Deep equals
Bun.deepEquals({ a: 1 }, { a: 1 }); // true
// Escape HTML
Bun.escapeHTML("<script>alert('xss')</script>");
// Resolve import path
const path = Bun.resolveSync("./module", import.meta.dir);
Framework Reference
Express Middleware Patterns
import express from 'express';
import helmet from 'helmet';
import compression from 'compression';
import rateLimit from 'express-rate-limit';
const app = express();
// Security middleware
app.use(helmet());
app.use(compression());
// Rate limiting
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
standardHeaders: true,
legacyHeaders: false,
});
app.use('/api/', limiter);
// Request logging
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
console.log(`${req.method} ${req.url} ${res.statusCode} ${Date.now() - start}ms`);
});
next();
});
// Error handling middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
error: {
message: err.message,
...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
},
});
});
Fastify Plugin Architecture
import Fastify from 'fastify';
import fastifySwagger from '@fastify/swagger';
import fastifySwaggerUi from '@fastify/swagger-ui';
import fastifyCors from '@fastify/cors';
const fastify = Fastify({
logger: {
level: 'info',
transport: {
target: 'pino-pretty',
},
},
});
// Register plugins
await fastify.register(fastifyCors, { origin: true });
await fastify.register(fastifySwagger, {
openapi: {
info: {
title: 'My API',
version: '1.0.0',
},
},
});
await fastify.register(fastifySwaggerUi, {
routePrefix: '/docs',
});
// Custom plugin
const myPlugin = async (fastify, options) => {
fastify.decorate('db', options.database);
fastify.addHook('onRequest', async (request) => {
request.startTime = Date.now();
});
fastify.addHook('onResponse', async (request, reply) => {
const duration = Date.now() - request.startTime;
fastify.log.info({ duration, url: request.url }, 'request completed');
});
};
fastify.register(myPlugin, { database: db });
Hono Adapters and Middleware
import { Hono } from 'hono';
import { serve } from '@hono/node-server';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
import { secureHeaders } from 'hono/secure-headers';
import { jwt } from 'hono/jwt';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
const app = new Hono();
// Middleware stack
app.use('*', logger());
app.use('*', secureHeaders());
app.use('/api/*', cors());
// JWT authentication
app.use('/api/protected/*', jwt({ secret: process.env.JWT_SECRET }));
// Zod validation
const createUserSchema = z.object({
name: z.string().min(2).max(100),
email: z.string().email(),
});
app.post('/api/users',
zValidator('json', createUserSchema),
async (c) => {
const data = c.req.valid('json');
const user = await db.users.create(data);
return c.json(user, 201);
}
);
// Error handling
app.onError((err, c) => {
console.error(err);
return c.json({ error: err.message }, 500);
});
// Not found handler
app.notFound((c) => c.json({ error: 'Not found' }, 404));
// Node.js adapter
serve({ fetch: app.fetch, port: 3000 });
// Or export for Cloudflare Workers, Deno, Bun
export default app;
Testing Reference
Vitest vs Jest Comparison
| Feature | Vitest | Jest |
|---|---|---|
| Speed | 4x faster cold, instant HMR | Baseline |
| ESM Support | Native | Requires config |
| TypeScript | Native | Via ts-jest/babel |
| Configuration | vite.config.js | jest.config.js |
| Watch Mode | Instant rerun | Full rerun |
| Snapshot Testing | Yes | Yes |
| Coverage | v8/istanbul | istanbul |
| Concurrent Tests | Per-file default | Optional |
Vitest Mocking Patterns
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
import { fetchUser, createUser } from './user.js';
// Mock module
vi.mock('./database.js', () => ({
db: {
users: {
findById: vi.fn(),
create: vi.fn(),
},
},
}));
import { db } from './database.js';
describe('User functions', () => {
beforeEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
vi.restoreAllMocks();
});
it('fetches user from database', async () => {
const mockUser = { id: 1, name: 'John' };
db.users.findById.mockResolvedValue(mockUser);
const user = await fetchUser(1);
expect(db.users.findById).toHaveBeenCalledWith(1);
expect(user).toEqual(mockUser);
});
it('handles fetch errors', async () => {
db.users.findById.mockRejectedValue(new Error('DB Error'));
await expect(fetchUser(1)).rejects.toThrow('DB Error');
});
// Spy on existing implementation
it('spies on console.log', () => {
const spy = vi.spyOn(console, 'log');
console.log('test');
expect(spy).toHaveBeenCalledWith('test');
});
// Timer mocks
it('handles timers', async () => {
vi.useFakeTimers();
const callback = vi.fn();
setTimeout(callback, 1000);
vi.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalled();
vi.useRealTimers();
});
});
Build Tools Reference
Vite Configuration
// vite.config.js
import { defineConfig } from 'vite';
import { resolve } from 'path';
export default defineConfig({
build: {
target: 'es2022',
outDir: 'dist',
lib: {
entry: resolve(__dirname, 'src/index.js'),
name: 'MyLib',
formats: ['es', 'cjs'],
fileName: (format) => `index.${format === 'es' ? 'js' : 'cjs'}`,
},
rollupOptions: {
external: ['express', 'fastify'],
output: {
manualChunks: {
vendor: ['lodash-es'],
},
},
},
minify: 'esbuild',
sourcemap: true,
},
esbuild: {
target: 'es2022',
keepNames: true,
},
server: {
port: 3000,
hmr: true,
},
});
esbuild Direct Usage
// build.js
import * as esbuild from 'esbuild';
await esbuild.build({
entryPoints: ['src/index.js'],
bundle: true,
minify: true,
sourcemap: true,
target: ['es2022'],
platform: 'node',
format: 'esm',
outdir: 'dist',
external: ['express', 'pg'],
define: {
'process.env.NODE_ENV': '"production"',
},
});
// Watch mode
const ctx = await esbuild.context({
entryPoints: ['src/index.js'],
bundle: true,
outdir: 'dist',
});
await ctx.watch();
console.log('watching...');
Context7 Library Mappings
Primary Libraries
/nodejs/node - Node.js runtime
/expressjs/express - Express web framework
/fastify/fastify - Fastify web framework
/honojs/hono - Hono web framework
/koajs/koa - Koa web framework
Testing
/vitest-dev/vitest - Vitest testing framework
/jestjs/jest - Jest testing framework
/testing-library - Testing Library
Build Tools
/vitejs/vite - Vite build tool
/evanw/esbuild - esbuild bundler
/rollup/rollup - Rollup bundler
/biomejs/biome - Biome linter/formatter
/eslint/eslint - ESLint linter
Utilities
/lodash/lodash - Lodash utilities
/date-fns/date-fns - Date utilities
/axios/axios - HTTP client
/prisma/prisma - Prisma ORM
Security Best Practices
Input Validation
import { z } from 'zod';
const userSchema = z.object({
name: z.string().min(1).max(100).trim(),
email: z.string().email().toLowerCase(),
age: z.number().int().min(0).max(150).optional(),
});
function validateUser(input) {
const result = userSchema.safeParse(input);
if (!result.success) {
throw new Error(result.error.issues[0].message);
}
return result.data;
}
Environment Variable Validation
import { z } from 'zod';
const envSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']),
PORT: z.string().transform(Number).pipe(z.number().min(1).max(65535)),
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
});
const env = envSchema.parse(process.env);
export default env;
Secure HTTP Headers
import helmet from 'helmet';
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
},
},
crossOriginEmbedderPolicy: true,
crossOriginOpenerPolicy: true,
crossOriginResourcePolicy: { policy: "same-origin" },
hsts: { maxAge: 31536000, includeSubDomains: true },
}));
Last Updated: 2026-01-05 Version: 1.1.0
### examples.md
```markdown
# JavaScript Production-Ready Examples
## Full-Stack Application Setup
### Project Structure
my-api/ āāā src/ ā āāā index.js # Application entry point ā āāā config/ ā ā āāā index.js # Configuration loader ā ā āāā database.js # Database configuration ā āāā api/ ā ā āāā routes/ ā ā ā āāā index.js # Route aggregator ā ā ā āāā users.js # User routes ā ā ā āāā posts.js # Post routes ā ā āāā middleware/ ā ā āāā auth.js # Authentication ā ā āāā validate.js # Request validation ā ā āāā errorHandler.js ā āāā services/ ā ā āāā userService.js # Business logic ā ā āāā postService.js ā āāā repositories/ ā ā āāā userRepository.js # Data access ā ā āāā postRepository.js ā āāā utils/ ā āāā logger.js ā āāā helpers.js āāā test/ ā āāā unit/ ā āāā integration/ ā āāā fixtures/ āāā package.json āāā eslint.config.js āāā vitest.config.js āāā Dockerfile
### package.json
```json
{
"name": "my-api",
"version": "1.0.0",
"type": "module",
"engines": {
"node": ">=22.0.0"
},
"scripts": {
"dev": "node --watch src/index.js",
"start": "node src/index.js",
"test": "vitest",
"test:coverage": "vitest run --coverage",
"test:e2e": "vitest run --config vitest.e2e.config.js",
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix",
"format": "biome format --write src/",
"check": "biome check src/",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"fastify": "^5.0.0",
"@fastify/cors": "^10.0.0",
"@fastify/helmet": "^12.0.0",
"@fastify/rate-limit": "^10.0.0",
"@fastify/swagger": "^9.0.0",
"@fastify/swagger-ui": "^5.0.0",
"zod": "^3.23.0",
"pg": "^8.13.0",
"pino": "^9.5.0",
"pino-pretty": "^13.0.0",
"dotenv": "^16.4.0"
},
"devDependencies": {
"@biomejs/biome": "^1.9.0",
"eslint": "^9.15.0",
"@eslint/js": "^9.15.0",
"globals": "^15.12.0",
"vitest": "^2.1.0",
"@vitest/coverage-v8": "^2.1.0",
"typescript": "^5.7.0",
"@types/node": "^22.10.0"
}
}
Fastify Complete API Example
src/index.js
import Fastify from 'fastify';
import cors from '@fastify/cors';
import helmet from '@fastify/helmet';
import rateLimit from '@fastify/rate-limit';
import swagger from '@fastify/swagger';
import swaggerUi from '@fastify/swagger-ui';
import { config } from './config/index.js';
import { routes } from './api/routes/index.js';
import { errorHandler } from './api/middleware/errorHandler.js';
import { logger } from './utils/logger.js';
const app = Fastify({
logger: logger,
disableRequestLogging: false,
});
// Security plugins
await app.register(helmet);
await app.register(cors, {
origin: config.corsOrigins,
credentials: true,
});
await app.register(rateLimit, {
max: 100,
timeWindow: '1 minute',
});
// API documentation
await app.register(swagger, {
openapi: {
info: {
title: 'My API',
description: 'API documentation',
version: '1.0.0',
},
servers: [{ url: config.apiUrl }],
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
},
},
},
},
});
await app.register(swaggerUi, { routePrefix: '/docs' });
// Routes
await app.register(routes, { prefix: '/api/v1' });
// Error handling
app.setErrorHandler(errorHandler);
// Health check
app.get('/health', async () => ({ status: 'ok', timestamp: new Date().toISOString() }));
// Start server
const start = async () => {
try {
await app.listen({ port: config.port, host: '0.0.0.0' });
app.log.info(`Server running on http://localhost:${config.port}`);
} catch (err) {
app.log.error(err);
process.exit(1);
}
};
start();
export { app };
src/config/index.js
import { z } from 'zod';
import 'dotenv/config';
const envSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
PORT: z.string().transform(Number).default('3000'),
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
CORS_ORIGINS: z.string().default('http://localhost:3000'),
API_URL: z.string().url().default('http://localhost:3000'),
LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
});
const parsed = envSchema.safeParse(process.env);
if (!parsed.success) {
console.error('Invalid environment variables:');
console.error(parsed.error.flatten().fieldErrors);
process.exit(1);
}
export const config = {
nodeEnv: parsed.data.NODE_ENV,
port: parsed.data.PORT,
databaseUrl: parsed.data.DATABASE_URL,
jwtSecret: parsed.data.JWT_SECRET,
corsOrigins: parsed.data.CORS_ORIGINS.split(','),
apiUrl: parsed.data.API_URL,
logLevel: parsed.data.LOG_LEVEL,
isDev: parsed.data.NODE_ENV === 'development',
isProd: parsed.data.NODE_ENV === 'production',
};
src/api/routes/users.js
import { z } from 'zod';
import { userService } from '../../services/userService.js';
import { authenticate } from '../middleware/auth.js';
const createUserSchema = z.object({
name: z.string().min(2).max(100),
email: z.string().email(),
password: z.string().min(8).max(100),
});
const updateUserSchema = z.object({
name: z.string().min(2).max(100).optional(),
email: z.string().email().optional(),
});
const idParamSchema = z.object({
id: z.string().uuid(),
});
const querySchema = z.object({
page: z.string().transform(Number).default('1'),
limit: z.string().transform(Number).default('10'),
search: z.string().optional(),
});
export async function userRoutes(fastify) {
// List users
fastify.get('/', {
schema: {
tags: ['Users'],
querystring: querySchema,
response: {
200: {
type: 'object',
properties: {
users: { type: 'array' },
pagination: { type: 'object' },
},
},
},
},
handler: async (request, reply) => {
const query = querySchema.parse(request.query);
const result = await userService.list(query);
return result;
},
});
// Get user by ID
fastify.get('/:id', {
schema: {
tags: ['Users'],
params: idParamSchema,
},
handler: async (request, reply) => {
const { id } = idParamSchema.parse(request.params);
const user = await userService.getById(id);
if (!user) {
return reply.code(404).send({ error: 'User not found' });
}
return user;
},
});
// Create user
fastify.post('/', {
schema: {
tags: ['Users'],
body: createUserSchema,
},
handler: async (request, reply) => {
const data = createUserSchema.parse(request.body);
const user = await userService.create(data);
return reply.code(201).send(user);
},
});
// Update user (protected)
fastify.put('/:id', {
preHandler: [authenticate],
schema: {
tags: ['Users'],
security: [{ bearerAuth: [] }],
params: idParamSchema,
body: updateUserSchema,
},
handler: async (request, reply) => {
const { id } = idParamSchema.parse(request.params);
const data = updateUserSchema.parse(request.body);
const user = await userService.update(id, data);
return user;
},
});
// Delete user (protected)
fastify.delete('/:id', {
preHandler: [authenticate],
schema: {
tags: ['Users'],
security: [{ bearerAuth: [] }],
params: idParamSchema,
},
handler: async (request, reply) => {
const { id } = idParamSchema.parse(request.params);
await userService.delete(id);
return reply.code(204).send();
},
});
}
src/services/userService.js
import { userRepository } from '../repositories/userRepository.js';
import { hashPassword, verifyPassword } from '../utils/crypto.js';
class UserService {
async list({ page, limit, search }) {
const offset = (page - 1) * limit;
const [users, total] = await Promise.all([
userRepository.findMany({ offset, limit, search }),
userRepository.count({ search }),
]);
return {
users,
pagination: {
page,
limit,
total,
totalPages: Math.ceil(total / limit),
},
};
}
async getById(id) {
return userRepository.findById(id);
}
async create(data) {
// Check for existing email
const existing = await userRepository.findByEmail(data.email);
if (existing) {
throw new Error('Email already exists');
}
// Hash password
const hashedPassword = await hashPassword(data.password);
return userRepository.create({
...data,
password: hashedPassword,
});
}
async update(id, data) {
const user = await userRepository.findById(id);
if (!user) {
throw new Error('User not found');
}
if (data.email && data.email !== user.email) {
const existing = await userRepository.findByEmail(data.email);
if (existing) {
throw new Error('Email already exists');
}
}
return userRepository.update(id, data);
}
async delete(id) {
const user = await userRepository.findById(id);
if (!user) {
throw new Error('User not found');
}
return userRepository.delete(id);
}
async authenticate(email, password) {
const user = await userRepository.findByEmail(email);
if (!user) {
return null;
}
const valid = await verifyPassword(password, user.password);
if (!valid) {
return null;
}
return user;
}
}
export const userService = new UserService();
src/repositories/userRepository.js
import { db } from '../config/database.js';
class UserRepository {
async findMany({ offset, limit, search }) {
let query = `
SELECT id, name, email, created_at, updated_at
FROM users
`;
const params = [];
if (search) {
query += ` WHERE name ILIKE $1 OR email ILIKE $1`;
params.push(`%${search}%`);
}
query += ` ORDER BY created_at DESC LIMIT $${params.length + 1} OFFSET $${params.length + 2}`;
params.push(limit, offset);
const result = await db.query(query, params);
return result.rows;
}
async count({ search }) {
let query = `SELECT COUNT(*) FROM users`;
const params = [];
if (search) {
query += ` WHERE name ILIKE $1 OR email ILIKE $1`;
params.push(`%${search}%`);
}
const result = await db.query(query, params);
return parseInt(result.rows[0].count);
}
async findById(id) {
const result = await db.query(
`SELECT id, name, email, created_at, updated_at FROM users WHERE id = $1`,
[id]
);
return result.rows[0] || null;
}
async findByEmail(email) {
const result = await db.query(
`SELECT id, name, email, password, created_at, updated_at FROM users WHERE email = $1`,
[email]
);
return result.rows[0] || null;
}
async create(data) {
const result = await db.query(
`INSERT INTO users (name, email, password)
VALUES ($1, $2, $3)
RETURNING id, name, email, created_at, updated_at`,
[data.name, data.email, data.password]
);
return result.rows[0];
}
async update(id, data) {
const fields = [];
const values = [];
let paramIndex = 1;
for (const [key, value] of Object.entries(data)) {
if (value !== undefined) {
fields.push(`${key} = $${paramIndex}`);
values.push(value);
paramIndex++;
}
}
if (fields.length === 0) {
return this.findById(id);
}
fields.push(`updated_at = NOW()`);
values.push(id);
const result = await db.query(
`UPDATE users SET ${fields.join(', ')} WHERE id = $${paramIndex}
RETURNING id, name, email, created_at, updated_at`,
values
);
return result.rows[0];
}
async delete(id) {
await db.query(`DELETE FROM users WHERE id = $1`, [id]);
}
}
export const userRepository = new UserRepository();
Testing Examples
vitest.config.js
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'node',
include: ['test/**/*.test.js'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
include: ['src/**/*.js'],
exclude: ['src/index.js'],
thresholds: {
lines: 80,
functions: 80,
branches: 75,
statements: 80,
},
},
setupFiles: ['test/setup.js'],
},
});
test/setup.js
import { beforeAll, afterAll, afterEach, vi } from 'vitest';
// Mock environment variables
process.env.NODE_ENV = 'test';
process.env.DATABASE_URL = 'postgres://test:test@localhost:5432/test';
process.env.JWT_SECRET = 'test-secret-key-at-least-32-characters';
process.env.PORT = '3001';
// Global test setup
beforeAll(async () => {
// Setup test database, start test server, etc.
});
afterAll(async () => {
// Cleanup
});
afterEach(() => {
vi.clearAllMocks();
});
test/unit/userService.test.js
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { userService } from '../../src/services/userService.js';
import { userRepository } from '../../src/repositories/userRepository.js';
// Mock repository
vi.mock('../../src/repositories/userRepository.js', () => ({
userRepository: {
findMany: vi.fn(),
count: vi.fn(),
findById: vi.fn(),
findByEmail: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
},
}));
// Mock crypto utilities
vi.mock('../../src/utils/crypto.js', () => ({
hashPassword: vi.fn((p) => `hashed_${p}`),
verifyPassword: vi.fn((plain, hashed) => hashed === `hashed_${plain}`),
}));
describe('UserService', () => {
const mockUser = {
id: '123e4567-e89b-12d3-a456-426614174000',
name: 'John Doe',
email: 'john@example.com',
created_at: new Date(),
updated_at: new Date(),
};
beforeEach(() => {
vi.clearAllMocks();
});
describe('list', () => {
it('returns paginated users', async () => {
userRepository.findMany.mockResolvedValue([mockUser]);
userRepository.count.mockResolvedValue(1);
const result = await userService.list({ page: 1, limit: 10 });
expect(result.users).toHaveLength(1);
expect(result.pagination).toEqual({
page: 1,
limit: 10,
total: 1,
totalPages: 1,
});
});
it('calculates offset correctly', async () => {
userRepository.findMany.mockResolvedValue([]);
userRepository.count.mockResolvedValue(0);
await userService.list({ page: 3, limit: 20 });
expect(userRepository.findMany).toHaveBeenCalledWith({
offset: 40,
limit: 20,
search: undefined,
});
});
});
describe('create', () => {
it('creates user with hashed password', async () => {
userRepository.findByEmail.mockResolvedValue(null);
userRepository.create.mockResolvedValue(mockUser);
const result = await userService.create({
name: 'John Doe',
email: 'john@example.com',
password: 'password123',
});
expect(userRepository.create).toHaveBeenCalledWith({
name: 'John Doe',
email: 'john@example.com',
password: 'hashed_password123',
});
expect(result).toEqual(mockUser);
});
it('throws on duplicate email', async () => {
userRepository.findByEmail.mockResolvedValue(mockUser);
await expect(
userService.create({
name: 'Jane',
email: 'john@example.com',
password: 'password',
})
).rejects.toThrow('Email already exists');
});
});
describe('authenticate', () => {
it('returns user on valid credentials', async () => {
const userWithPassword = { ...mockUser, password: 'hashed_password123' };
userRepository.findByEmail.mockResolvedValue(userWithPassword);
const result = await userService.authenticate('john@example.com', 'password123');
expect(result).toEqual(userWithPassword);
});
it('returns null on invalid password', async () => {
const userWithPassword = { ...mockUser, password: 'hashed_password123' };
userRepository.findByEmail.mockResolvedValue(userWithPassword);
const result = await userService.authenticate('john@example.com', 'wrongpassword');
expect(result).toBeNull();
});
it('returns null on non-existent user', async () => {
userRepository.findByEmail.mockResolvedValue(null);
const result = await userService.authenticate('unknown@example.com', 'password');
expect(result).toBeNull();
});
});
});
test/integration/users.test.js
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
import { app } from '../../src/index.js';
import { db } from '../../src/config/database.js';
describe('Users API', () => {
beforeAll(async () => {
await app.ready();
});
afterAll(async () => {
await app.close();
await db.end();
});
beforeEach(async () => {
// Clean up database
await db.query('DELETE FROM users');
});
describe('POST /api/v1/users', () => {
it('creates a new user', async () => {
const response = await app.inject({
method: 'POST',
url: '/api/v1/users',
payload: {
name: 'John Doe',
email: 'john@example.com',
password: 'password123',
},
});
expect(response.statusCode).toBe(201);
const body = JSON.parse(response.body);
expect(body).toMatchObject({
name: 'John Doe',
email: 'john@example.com',
});
expect(body.id).toBeDefined();
expect(body.password).toBeUndefined();
});
it('returns 400 for invalid email', async () => {
const response = await app.inject({
method: 'POST',
url: '/api/v1/users',
payload: {
name: 'John Doe',
email: 'invalid-email',
password: 'password123',
},
});
expect(response.statusCode).toBe(400);
});
});
describe('GET /api/v1/users', () => {
it('returns paginated users', async () => {
// Create test users
await db.query(
`INSERT INTO users (name, email, password) VALUES
('User 1', 'user1@test.com', 'hashed'),
('User 2', 'user2@test.com', 'hashed')`
);
const response = await app.inject({
method: 'GET',
url: '/api/v1/users?page=1&limit=10',
});
expect(response.statusCode).toBe(200);
const body = JSON.parse(response.body);
expect(body.users).toHaveLength(2);
expect(body.pagination.total).toBe(2);
});
});
});
Hono Edge Example
src/index.js (Edge/Serverless)
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
import { secureHeaders } from 'hono/secure-headers';
import { prettyJSON } from 'hono/pretty-json';
import { compress } from 'hono/compress';
import { cache } from 'hono/cache';
import { jwt } from 'hono/jwt';
import { zValidator } from '@hono/zod-validator';
import { z } from 'zod';
const app = new Hono();
// Middleware
app.use('*', logger());
app.use('*', secureHeaders());
app.use('*', compress());
app.use('/api/*', cors());
app.use('/api/*', prettyJSON());
// Cache static responses
app.get('/api/health', cache({ cacheName: 'health', cacheControl: 'max-age=60' }));
// Health check
app.get('/api/health', (c) => c.json({ status: 'ok', timestamp: Date.now() }));
// JWT protected routes
app.use('/api/protected/*', jwt({ secret: Bun.env.JWT_SECRET }));
// Validation schemas
const createPostSchema = z.object({
title: z.string().min(1).max(200),
content: z.string().min(1),
tags: z.array(z.string()).optional(),
});
// Routes with validation
app.post('/api/posts',
jwt({ secret: Bun.env.JWT_SECRET }),
zValidator('json', createPostSchema),
async (c) => {
const payload = c.get('jwtPayload');
const data = c.req.valid('json');
// Create post logic
const post = {
id: crypto.randomUUID(),
...data,
authorId: payload.sub,
createdAt: new Date().toISOString(),
};
return c.json(post, 201);
}
);
app.get('/api/posts', async (c) => {
const page = Number(c.req.query('page') || '1');
const limit = Number(c.req.query('limit') || '10');
// Fetch posts logic
return c.json({
posts: [],
pagination: { page, limit, total: 0 },
});
});
// Error handling
app.onError((err, c) => {
console.error(`${err}`);
return c.json({ error: err.message }, 500);
});
app.notFound((c) => c.json({ error: 'Not Found' }, 404));
// Export for different runtimes
export default app;
// Bun
// Bun.serve({ port: 3000, fetch: app.fetch });
// Node.js
// import { serve } from '@hono/node-server';
// serve({ fetch: app.fetch, port: 3000 });
ESLint 9 Flat Config
eslint.config.js
import js from '@eslint/js';
import globals from 'globals';
export default [
{
ignores: ['dist/', 'node_modules/', 'coverage/'],
},
js.configs.recommended,
{
files: ['**/*.js'],
languageOptions: {
ecmaVersion: 2025,
sourceType: 'module',
globals: {
...globals.node,
...globals.es2025,
},
},
rules: {
// Best practices
'no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
'no-console': ['warn', { allow: ['warn', 'error', 'info'] }],
'prefer-const': 'error',
'no-var': 'error',
'object-shorthand': 'error',
'prefer-template': 'error',
'prefer-arrow-callback': 'error',
// Code style
'arrow-body-style': ['error', 'as-needed'],
'no-multiple-empty-lines': ['error', { max: 1 }],
'eol-last': ['error', 'always'],
// Error prevention
'no-return-await': 'error',
'require-await': 'error',
'no-async-promise-executor': 'error',
'no-promise-executor-return': 'error',
},
},
{
files: ['test/**/*.js'],
languageOptions: {
globals: {
...globals.vitest,
},
},
rules: {
'no-unused-expressions': 'off',
},
},
];
Dockerfile
# Build stage
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
# Production stage
FROM node:22-alpine
WORKDIR /app
# Security: run as non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
COPY --from=builder /app/node_modules ./node_modules
COPY . .
# Set ownership
RUN chown -R nodejs:nodejs /app
USER nodejs
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "src/index.js"]
Last Updated: 2025-12-22 Version: 1.0.0
Source: https://github.com/modu-ai/moai-adk#src~moai_adk~templates~.claude~skills~moai-lang-javascript
Content curated from original sources, copyright belongs to authors
User Rating
USER RATING
WORKS WITH