Telegram Bot with Node.js: Implementing CRUD-L Operations Using Command Arguments
Publication date: 2024-10-14In this post, I’ll share my journey into Telegram bot development, following up on a previous post where I created a basic Node.js bot with standard commands like /start, /help, and /settings using the grammY library. This time, we’ll go a step further by building a bot capable of managing data in a database using the CRUD-L pattern (Create, Read, Update, Delete, and List) through command arguments. While this approach is simple, it offers a great starting point for personal projects.
The demo bot, @flancer64/tg-demo-crudl, allows users to manage a list of contacts and phone numbers in a database using the following commands:
/create <name> <phone number>: Adds a new contact to the list (e.g.,/create John 123456789)/read <id>: Retrieves information about a specific contact (e.g.,/read 1)/update <id> <new name> <new phone number>: Updates an existing contact (e.g.,/update 1 Jane 987654321)/delete <id>: Deletes a contact (e.g.,/delete 1)/list: Displays all saved contacts
Technologies Used
- grammY: A modern JavaScript framework for interacting with the Telegram API.
- Knex.js: A database abstraction layer (DBAL) for Node.js that supports multiple databases such as SQLite, PostgreSQL, and MySQL.
- @teqfw/di: A dependency injection package to manage the flexibility and scalability of the application.
- @flancer32/teq-telegram-bot: A package that handles common operations like long polling, webhook management, and bot configuration.
Creating a Typical Command for the Bot
In this section, I won’t go into the details of how to set up the bot itself. If you need help setting up a basic bot, you can refer to my previous article. Instead, we’ll focus on how to create a typical command for handling CRUD operations. We’ll use /create as an example, and this approach can be easily extended to implement other commands.
Dependency Injection
The ES6 module (./src/Back/Bot/Cmd/Create.js) is written in a way that allows the Object Container to manage dependencies based on the instructions in the module. The following dependencies are injected:
export default class Demo_Crudl_Back_Bot_Cmd_Create {
constructor(
{
TeqFw_Core_Shared_Api_Logger$$: logger,
TeqFw_Db_Back_RDb_IConnect$: conn,
TeqFw_Db_Back_Api_RDb_CrudEngine$: crud,
Demo_Crudl_Back_Store_RDb_Schema_Phone$: rdbPhone,
}
) {}
} * Logger (TeqFw_Core_Shared_Api_Logger): For logging the execution of commands. * Database Connection (TeqFw_Db_Back_RDb_IConnect): To handle database transactions. * CRUD Engine (TeqFw_Db_Back_Api_RDb_CrudEngine): To perform basic database operations. * DTO for Database Structure (Demo_Crudl_Back_Store_RDb_Schema_Phone): A Data Transfer Object (DTO) that defines the database structure.
The first three dependencies are standard tools from my TeqFW toolkit, while the last one is specific to the domain of the project.
DTO for the Database
The Demo_Crudl_Back_Store_RDb_Schema_Phone DTO describes a simple structure with four fields:
class Dto {
date_created;
id;
name;
phone;
}
This DTO corresponds to the following table in an SQLite database:
CREATE TABLE main.phone
(
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name VARCHAR(255) NOT NULL,
phone VARCHAR(255) NOT NULL,
date_created DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL
); This structure is sufficient to give you an idea of how the data is organized in the database. While the demo bot uses SQLite, Knex.js, a database abstraction layer (DBAL), allows you to work with PostgreSQL, MariaDB/MySQL, MS SQL, and Oracle as well. You just need to install the appropriate npm package for the desired database system.
The Knex.js connection settings for the specific database are configured in the ./cfg/local.json file (the configuration template can be found in ./cfg/init.json). In the demo version, SQLite is used, with the database file located at ./var/data.db. The following commands are used to interact with the database:
$ ./bin/tequila.mjs db-init $ ./bin/tequila.mjs db-export -f ./var/data.json $ ./bin/tequila.mjs db-import -f ./var/data.json The command names are self-explanatory:db-initinitializes the database structure (table), whiledb-exportanddb-importhandle data export and import in JSON format, respectively.
Template for Handling Telegram Commands
Since our bot uses the grammY framework, a typical command handler works with the platform’s ctx object. Here’s a skeleton of the handler:
const handler = async (ctx) => {
let msg = ‘The command has failed.’;
const from = ctx.message.from;
logger.info(Command has been received from user '${from.username}' (id:${from.id}));
const trx = await conn.startTransaction();
try {
// const parts = ctx.message.text.split(’ ’);
// …
await trx.commit();
} catch (e) {
await trx.rollback();
msg = e.toString();
logger.error(msg);
}
// https://core.telegram.org/bots/api#sendmessage
await ctx.reply(msg, {
parse_mode: ‘HTML’,
}); };
The algorithm for the handler’s execution is as follows:
- Log the received command.
- Start a transaction for working with the database.
- Perform the necessary database operations within the transaction and form a response to the received command.
- Commit the changes to the database.
- Form a message for the user indicating the successful completion of the command.
- In case of errors, return an error message to the user.
Executing the Command
The following code demonstrates how the /create command is processed — extracting the data from the command and saving it to the database:
try {
const parts = ctx.message.text.split(’ ’);
const dto = rdbPhone.createDto();
dto.name = parts[1];
dto.phone = parts[2];
const {[A_PHONE.ID]: id} = await crud.create(trx, rdbPhone, dto);
await trx.commit();
msg = New record #${id} has been created.;
logger.info(msg);
} catch (e) {…}
I use my own wrapper for the DBAL Knex.js, which allows me to perform basic operations (CRUD) with DTOs. If you are using a different library for working with the database (e.g., Sequelize or Prisma), the code here will be different. However, the rest of the surrounding logic can remain the same (except for transaction handling).
Example of Bot Operation
After creating the bot in BotFather, obtaining the API key, and configuring the bot (./cfg/local.json), you need to create the database tables:
$ ./bin/tequila.mjs db-init Start the bot in long polling mode: $ ./bin/tequila.mjs tg-bot-start or $ npm start ### Get the Help
Accessing bot commands
Create
Adding a new record
Read
Retrieving a record
Update
Modifying a record
List
Listing all records
Delete
Deleting a record
Conclusion
In this article, I covered the process of creating a simple Telegram bot using Node.js and the grammY library, which supports CRUD-L operations (Create, Read, Update, Delete, and List). We explored how command arguments can be used to manipulate data in a database. Despite its simplicity, this approach opens up opportunities for personal projects where complex business logic isn’t required, but ease of implementation is key.
If you are looking for a simple yet powerful solution for working with databases through Telegram bots, this project can be a great starting point for bringing your ideas to life.
Happy coding!
If you enjoyed this article, please give it a clap and follow me for more content!
Stay connected:
Thank you for your support!