Skip to content

Services

Services in GamanJS hold your business logic. Created with composeService, services are independent from the HTTP layer — pure logic.

src/module/services/AppService.ts
import { composeService } from 'gaman/compose';
export default composeService(() => ({
WelcomeMessage() {
return '❤️ Welcome to GamanJS';
},
}));

composeService takes a factory function that returns an object containing logic methods.

src/module/services/UserService.ts
import { composeService } from 'gaman/compose';
interface User {
id: number;
name: string;
email: string;
}
const users: User[] = [
{ id: 1, name: 'Angga', email: 'angga@example.com' },
{ id: 2, name: 'Budi', email: 'budi@example.com' },
];
export default composeService(() => ({
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;
},
delete(id: number): boolean {
const idx = users.findIndex((u) => u.id === id);
if (idx === -1) return false;
users.splice(idx, 1);
return true;
},
}));

Services can also receive dependencies from other services:

src/module/services/EmailService.ts
import { composeService } from 'gaman/compose';
export default composeService(() => ({
sendWelcome(email: string) {
console.log(`Sending welcome email to ${email}`);
},
sendResetPassword(email: string, token: string) {
console.log(`Sending reset password email to ${email}`);
},
}));
src/module/services/AuthService.ts
import { composeService } from 'gaman/compose';
import type { RT } from 'gaman/types';
import EmailService from './EmailService';
export default composeService(
(emailService: RT<typeof EmailService> = EmailService()) => ({
register(name: string, email: string) {
// ... register logic
emailService.sendWelcome(email);
return { name, email };
},
resetPassword(email: string) {
const token = crypto.randomUUID();
emailService.sendResetPassword(email, token);
},
}),
);
import { composeController } from 'gaman/compose';
import UserService from '../services/UserService';
import type { RT } from 'gaman/types';
export default composeController(
(userService: RT<typeof UserService> = UserService()) => ({
GetAll(ctx) {
return Res.send(userService.findAll());
},
}),
);
  • 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
  • Separation of concerns: Service = business logic, Controller = HTTP handling