Overview
The service layer contains all business logic and database operations for the WhatsApp Assistant Bot. Services are implemented as static classes that provide methods for CRUD operations and domain-specific functionality. All services use Drizzle ORM for database operations and follow consistent patterns for error handling and data validation.Service Architecture
Design Principles
- Static Methods: Services use static methods (no instantiation required)
- Database Abstraction: All database queries go through Drizzle ORM
- Type Safety: Full TypeScript type inference from database schema
- Single Responsibility: Each service handles one domain
Database Connection
Services import the database instance fromsrc/db/index.ts:
TodoService
Manages todo list items with support for completion tracking and bulk operations. Location:src/services/TodoService.ts
Methods
create(userId: string, chatId: string, task: string): Promise<Todo>
Creates a new todo item.
list(chatId: string, includeCompleted = false): Promise<Todo[]>
Lists todos for a specific chat. By default, only shows incomplete todos.
listAllUserTodos(userId: string, includeCompleted = false): Promise<Todo[]>
Lists all todos for a user across all chats.
complete(chatId: string, todoId: string): Promise<void>
Marks a todo as complete and sets the completion timestamp.
delete(chatId: string, todoId: string): Promise<void>
Permanently deletes a todo item.
clearCompleted(chatId: string): Promise<number>
Deletes all completed todos in a chat and returns the count.
getById(chatId: string, todoId: string): Promise<Todo | null>
Retrieves a specific todo by ID.
Todo Type
NoteService
Handles note creation, retrieval, and search functionality. Location:src/services/NoteService.ts
Methods
create(userId: string, content: string): Promise<Note>
Creates a new note for a user.
list(userId: string): Promise<Note[]>
Returns all notes for a user, sorted by most recently updated.
getById(userId: string, noteId: string): Promise<Note | null>
Retrieves a specific note by ID.
update(userId: string, noteId: string, content: string): Promise<Note | null>
Updates the content of an existing note.
delete(userId: string, noteId: string): Promise<boolean>
Deletes a note and returns true if successful.
search(userId: string, query: string): Promise<Note[]>
Searches notes using case-insensitive pattern matching (PostgreSQL ILIKE).
Note Type
ReminderService
Manages scheduled reminders with notification integration. Location:src/services/ReminderService.ts
Methods
create(userId: string, task: string, time: Date, notifyUsers: string[] = []): Promise<Reminder>
Creates a reminder and schedules it for notification.
NotificationService.
list(userId: string, includeCompleted = false): Promise<Reminder[]>
Lists reminders for a user, sorted by time.
delete(userId: string, reminderId: string): Promise<void>
Deletes a reminder and cancels its scheduled notification.
clearCompleted(userId: string): Promise<number>
Removes all completed reminders for a user.
Reminder Type
TimerService
Manages countdown timers with cron-based scheduling. Location:src/services/TimerService.ts
Lifecycle Methods
initialize(socket: WASocket): void
Initializes the service and loads active timers from the database.
cleanup(): void
Stops all active timers and clears resources.
Timer Methods
create(userId: string, duration: number): Promise<Timer>
Creates and schedules a timer (duration in minutes).
list(userId: string): Promise<Timer[]>
Lists all active timers for a user.
cancel(userId: string, timerId: string): Promise<void>
Cancels a timer and stops its notification.
Implementation Details
- Uses
node-cronfor scheduling timer completions - Maintains in-memory map of active cron jobs
- Automatically loads timers on startup
- Sends WhatsApp notification when timer completes
Timer Type
NotificationService
Central service for scheduling and sending notifications. Location:src/services/NotificationService.ts
Lifecycle Methods
initialize(socket: WASocket): void
Initializes the service and loads all active notifications.
cleanup(): void
Stops all scheduled jobs and clears resources.
Scheduling Methods
scheduleReminder(reminder: Reminder): Promise<void>
Schedules a reminder notification using cron.
scheduleTimer(timer: Timer): Promise<void>
Schedules a timer completion notification.
cancelReminder(reminderId: string): void
Cancels a scheduled reminder notification.
cancelTimer(timerId: string): void
Cancels a scheduled timer notification.
Implementation Details
- Uses cron expressions to schedule exact notification times
- Maintains separate maps for reminder and timer jobs
- Automatically marks reminders/timers as completed after sending
- Handles multiple recipients for reminders
UserService
Manages user accounts and settings. Location:src/services/UserService.ts
Methods
getOrCreate(userId: string): Promise<User>
Retrieves a user or creates one if it doesn’t exist.
updateLastActive(userId: string): Promise<void>
Updates the user’s last active timestamp.
updateSettings(userId: string, settings: Partial<UserSettings>): Promise<void>
Updates user settings.
getSettings(userId: string): Promise<UserSettings>
Retrieves user settings (creates user if doesn’t exist).
exists(userId: string): Promise<boolean>
Checks if a user exists in the database.
User Types
Creating a New Service
export const myTable = pgTable('my_table', {
id: uuid('id').defaultRandom().primaryKey(),
userId: text('user_id').notNull(),
data: text('data').notNull(),
createdAt: timestamp('created_at').defaultNow(),
});
export type MyData = typeof myTable.$inferSelect;
export type NewMyData = typeof myTable.$inferInsert;
import { db, myTable, MyData } from '../db/index.js';
import { eq } from 'drizzle-orm';
export class MyService {
static async create(userId: string, data: string): Promise<MyData> {
const [result] = await db.insert(myTable)
.values({ userId, data })
.returning();
return result;
}
static async list(userId: string): Promise<MyData[]> {
return await db.select()
.from(myTable)
.where(eq(myTable.userId, userId));
}
// Add more methods as needed
}
Best Practices
1. Always Use Returning
When inserting or updating, use.returning() to get the result:
2. Use Drizzle Query Builders
3. Handle Null Results
4. Use Transactions for Related Operations
Next Steps
- Learn how to add new commands that use services
- Review the System Architecture for overall design
- Check the Drizzle ORM documentation for advanced queries