Services
Services in GamanJS hold your business logic. Created with composeService, services are independent from the HTTP layer — pure logic.
Creating a Service
Section titled “Creating a Service”The recommended pattern is to export the service instance as a constant and export its return type for easier dependency injection in controllers.
import { composeService } from 'gaman/compose';import type { RT } from 'gaman/types';
export const AppService = composeService(() => { /** * You can put your private logic, state, or database connections here */
return { WelcomeMessage() { return '❤️ Welcome to GamanJS'; }, };});
// Export the return type of the serviceexport type AppService = RT<typeof AppService>;Example: User Service
Section titled “Example: User Service”import { composeService } from 'gaman/compose';import type { RT } from 'gaman/types';
interface User { id: number; name: string; email: string;}
const users: User[] = [ { id: 1, name: 'Angga', email: 'angga@example.com' },];
export const UserService = composeService(() => { return { findAll(): User[] { return users; },
findById(id: number): User | undefined { return users.find((u) => u.id === id); },
create(data: Omit<User, 'id'>): User { const newUser = { ...data, id: users.length + 1 }; users.push(newUser); return newUser; }, };});
export type UserService = RT<typeof UserService>;Service with Dependencies
Section titled “Service with Dependencies”Services can also receive dependencies from other services:
import { composeService } from 'gaman/compose';import type { RT } from 'gaman/types';import { EmailService } from './EmailService';
export const AuthService = composeService( (emailService: RT<typeof EmailService> = EmailService()) => { return { register(name: string, email: string) { // ... register logic emailService.sendWelcome(email); return { name, email }; }, }; });
export type AuthService = RT<typeof AuthService>;Using in a Controller
Section titled “Using in a Controller”When using a service in a controller, use the recommended destructuring pattern with a Deps type.
import { composeController } from 'gaman/compose';import { UserService } from '../services/UserService';
export type Deps = { userService: UserService;};
export default composeController(({ userService }: Deps) => ({ GetAll(ctx) { const users = userService.findAll(); return ctx.send(users).ok(); },}));- Services do not have access to
ctx— this is intentional so logic stays independent from HTTP. - One service can be used by multiple controllers.
- Services can depend on each other.
- Use
export type Name = RT<typeof Name>to make DI clean and type-safe. - Separation of concerns: Service = business logic, Controller = HTTP handling.