Documentation Index
Fetch the complete documentation index at: https://mintlify.com/rifandani/be-monorepo/llms.txt
Use this file to discover all available pages before exploring further.
@workspace/core
The @workspace/core package is the shared foundation for the BE Monorepo, providing common utilities, type definitions, HTTP services, and API clients used across all applications.
Overview
This package is structured as a modular library with the following exports:
- apis/ - API client repositories with Zod validation
- assets/ - Shared static assets (images, etc.)
- constants/ - Common constants and type definitions
- services/ - Shared services (HTTP client)
- types/ - TypeScript type definitions
- utils/ - Utility functions for common operations
Installation
The package is already available in the monorepo workspace:
{
"dependencies": {
"@workspace/core": "workspace:*"
}
}
Package Structure
APIs
API repositories provide typed HTTP clients with built-in Zod validation.
Auth API
Location: packages/core/src/apis/auth.ts:1
import { Http } from "@workspace/core/services/http";
import { authRepositories } from "@workspace/core/apis/auth";
const http = new Http({
prefixUrl: "https://api.example.com",
timeout: 30000,
});
const auth = authRepositories(http);
// Sign in with email
const response = await auth.signInEmail({
json: {
email: "user@example.com",
password: "securepass123",
rememberMe: true,
},
});
console.log(response.json.token);
Available Methods:
getSession() - GET /api/auth/get-session
signInEmail() - POST /api/auth/sign-in/email
signUpEmail() - POST /api/auth/sign-up/email
signOut() - POST /api/auth/sign-out
Schemas:
All API methods include Zod schemas for request/response validation:
import {
authSignInEmailRequestSchema,
authSignInEmailResponseSchema,
type AuthSignInEmailRequestSchema,
type AuthSignInEmailResponseSchema,
} from "@workspace/core/apis/auth";
// Request validation
const validatedRequest = authSignInEmailRequestSchema.parse({
email: "user@example.com",
password: "password123",
rememberMe: false,
});
Constants
Core Constants
Location: packages/core/src/constants/core.ts:1
import {
kilobyteMultiplier,
megabyteMultiplier,
gigabyteMultiplier,
indoTimezone,
} from "@workspace/core/constants/core";
// File size calculations
const fileSizeInMB = fileSizeInBytes / megabyteMultiplier;
// Timezone handling
const timezone = indoTimezone[0]; // "WIB"
Exported Constants:
kilobyteMultiplier - 1024
megabyteMultiplier - 1,048,576
gigabyteMultiplier - 1,073,741,824
indoTimezone - ["WIB", "WITA", "WIT"]
HTTP Constants
Location: packages/core/src/constants/http.ts:1
import {
HTTP_STATUS_CODES,
type StatusCode,
type ClientErrorStatusCode,
} from "@workspace/core/constants/http";
// Check status codes
if (response.status === HTTP_STATUS_CODES.UNAUTHORIZED) {
// Handle unauthorized access
}
Status Code Types:
InfoStatusCode - 1xx status codes
SuccessStatusCode - 2xx status codes
RedirectStatusCode - 3xx status codes
ClientErrorStatusCode - 4xx status codes
ServerErrorStatusCode - 5xx status codes
StatusCode - Union of all status code types
Services
HTTP Service
Location: packages/core/src/services/http.ts:1
A wrapper around ky for making HTTP requests.
import { Http } from "@workspace/core/services/http";
// Create HTTP client
const http = new Http({
prefixUrl: process.env.API_BASE_URL,
timeout: 30000,
headers: {
"Content-Type": "application/json",
},
hooks: {
beforeRequest: [
(request) => {
// Add auth token
const token = getAuthToken();
if (token) {
request.headers.set("Authorization", `Bearer ${token}`);
}
},
],
},
});
// Make requests
const response = await http.instance.get("users/me");
const data = await response.json();
// Update configuration
http.updateConfig({
headers: {
"X-Custom-Header": "value",
},
});
// Reset configuration
http.resetConfig({
prefixUrl: "https://new-api.example.com",
});
Methods:
constructor(config) - Create new HTTP instance
updateConfig(newConfig) - Extend current configuration
resetConfig(newConfig) - Replace configuration entirely
Types
Location: packages/core/src/types/core.ts:1
import type { URLSearchParamsInit } from "@workspace/core/types/core";
const params: URLSearchParamsInit = {
query: "search term",
page: "1",
filters: ["active", "verified"],
};
const searchParams = new URLSearchParams(params);
Exported Types:
URLSearchParamsInit - Flexible URL search params type
Utils
Core Utilities
Location: packages/core/src/utils/core.ts:1
import {
clamp,
toCamelCase,
toSnakeCase,
objectToFormData,
deepReadObject,
indonesianPhoneNumberFormat,
removeLeadingZeros,
removeLeadingWhitespace,
} from "@workspace/core/utils/core";
// Clamp values
const clamped = clamp({ value: 150, min: 0, max: 100 }); // 100
// Case conversion
const camelData = toCamelCase<{ firstName: string }>({
first_name: "John",
});
console.log(camelData.firstName); // "John"
const snakeData = toSnakeCase({ firstName: "John" });
console.log(snakeData.first_name); // "John"
// FormData conversion
const formData = objectToFormData({
name: "John Doe",
age: 30,
avatar: fileInput.files[0],
address: {
street: "123 Main St",
city: "Jakarta",
},
tags: ["developer", "designer"],
});
// Deep object reading
const value = deepReadObject(
{ user: { profile: { name: "John" } } },
"user.profile.name"
); // "John"
// Phone number formatting
const formatted = indonesianPhoneNumberFormat("+628127363636");
// "+62-812-7363-6365"
// Remove leading zeros
const cleaned = removeLeadingZeros("00123"); // "123"
Available Functions:
clamp({ value, min, max }) - Clamp value to range
toCamelCase<T>(obj) - Convert object keys to camelCase
toSnakeCase<T>(obj) - Convert object keys to snake_case
objectToFormData(obj, options?) - Convert object to FormData
objectToFormDataArrayWithComma(obj, options?) - Convert with comma-separated arrays
deepReadObject<T>(obj, path, defaultValue?) - Safely read nested values
indonesianPhoneNumberFormat(phoneNumber) - Format Indonesian phone numbers
removeLeadingZeros(value) - Remove leading zeros from strings
removeLeadingWhitespace(value?) - Remove leading whitespace
Date Utilities
Location: packages/core/src/utils/date.ts:1
import {
isValidTimezoneIANAString,
getLocalTimeZone,
} from "@workspace/core/utils/date";
// Validate timezone
if (isValidTimezoneIANAString("Asia/Jakarta")) {
console.log("Valid timezone");
}
// Get local timezone
const timezone = getLocalTimeZone(); // "Asia/Jakarta"
Invariant Utility
Location: packages/core/src/utils/invariant.ts:1
Assertion function for runtime validation with TypeScript type narrowing.
import { invariant } from "@workspace/core/utils/invariant";
function processUser(user: User | null) {
invariant(user, "Expected user to be defined");
// TypeScript now knows user is User (not null)
console.log(user.name);
}
// With dynamic message
invariant(
items.length > 0,
() => `Expected items array to have elements, got ${items.length}`
);
Features:
- TypeScript assertion function for type narrowing
- Messages stripped in production for smaller bundles
- Supports string or function messages
Logger Utility
Location: packages/core/src/utils/logger.ts:1
Colored console logger with timestamps and severity levels.
import { logger } from "@workspace/core/utils/logger";
logger.debug("Debug message", { detail: "value" });
logger.log("Info message");
logger.warn("Warning message", error);
logger.error("Error occurred", error);
Output Format:
[14:30:45.123] INFO: Server started on port 3000
[14:30:46.456] WARN: Deprecated API endpoint used
[14:30:47.789] ERROR: Database connection failed
Methods:
debug(message, ...attributes) - Green debug logs
log(message, ...attributes) - Blue info logs
warn(message, ...attributes) - Yellow warning logs
error(message, ...attributes) - Red error logs
Assets
Location: packages/core/src/assets/
Shared static assets that can be imported across applications.
import logoImage from "@workspace/core/assets/images/logo.png";
Usage in Applications
The Hono app demonstrates typical usage patterns:
// apps/hono/src/node.ts:2
import { logger } from "@workspace/core/utils/logger";
import { PORT } from "@/core/constants/global.js";
import { app } from "./app.js";
const server = serve({ ...app, port: PORT }, (info) => {
logger.log(`Started development server: http://localhost:${info.port}`);
});
process.on("SIGTERM", () => {
server.close((err) => {
if (err) {
logger.error(err.message);
process.exit(1);
}
process.exit(0);
});
});
TypeScript Configuration
Applications extend the shared TypeScript configuration and map paths to the core package:
{
"extends": "@workspace/typescript-config/node.json",
"compilerOptions": {
"paths": {
"@workspace/core/*": ["../../packages/core/src/*"]
}
}
}
Dependencies
The core package uses these external dependencies:
- ky (1.14.3) - HTTP client
- radashi (12.7.1) - Utility functions
- type-fest (5.4.4) - TypeScript utility types
- zod (4.3.6) - Schema validation
Best Practices
API Client Usage
- Create a single HTTP instance per API base URL
- Use repository pattern for organizing endpoints
- Leverage Zod schemas for type safety
- Handle errors with try-catch blocks
import { Http } from "@workspace/core/services/http";
import { authRepositories } from "@workspace/core/apis/auth";
import { logger } from "@workspace/core/utils/logger";
const http = new Http({
prefixUrl: process.env.API_BASE_URL,
timeout: 30000,
});
const auth = authRepositories(http);
try {
const { json } = await auth.getSession();
if (json) {
logger.log("User authenticated", json.user);
}
} catch (error) {
logger.error("Failed to get session", error);
}
Utility Functions
- Use
invariant for runtime assertions and type narrowing
- Prefer
deepReadObject over manual property access for nested data
- Use case conversion utilities for API data transformation
- Use the logger for consistent, colored output
import { invariant } from "@workspace/core/utils/invariant";
import { deepReadObject } from "@workspace/core/utils/core";
import { logger } from "@workspace/core/utils/logger";
function processApiResponse(data: unknown) {
invariant(data && typeof data === "object", "Invalid API response");
const userId = deepReadObject<string>(data, "user.id", "anonymous");
logger.log("Processing user:", userId);
}
Type Safety
- Import both schema and type from APIs
- Use type assertions with Zod validation
- Leverage TypeScript’s type narrowing with invariant
import {
authSignInEmailRequestSchema,
type AuthSignInEmailRequestSchema
} from "@workspace/core/apis/auth";
// Validate at runtime
const request = authSignInEmailRequestSchema.parse(userInput);
// TypeScript knows the exact shape now
Development
Adding New APIs
- Create a new file in
src/apis/
- Define Zod schemas for entities, requests, and responses
- Create query keys for caching (if using React Query)
- Export a repository function that takes an Http instance
Adding New Utilities
- Add function to appropriate file in
src/utils/
- Include JSDoc comments with examples
- Export from the module
- Add tests if needed