Skip to content

Services

andreabbondanza edited this page Sep 6, 2023 · 3 revisions

Repository and Services

General

Services and Repositories are essentially the same thing with one big difference:

  • A Repository is usually used to interact with data sources like databases, files, etc. It's loaded one time when the server starts.
  • A Service is usually used to execute pieces of business logic in a context (for example you can have Basket's management functions). It's common that to execute its own tasks, a Service use Repositories.

Naming Repositories

You need to add the repository into the repositories folder and the file must follow this naming rule: myRepository.repository.ts (details here 👍)

Your repository will be loaded and initialized when server starts.

Repository

A repository is a class that interact with data sources, an example is this one that works with a MySql database (note, this isn't a full and working example, it's mean to give an idea of how a repository is done):

export class MYSqlRepository extends Repository
{
    //DB CONNECT INSTANCE
    private _connection: Connection | null = null;
    
    public async getConnection()
    {
        if (!this._connection)
            this._connection = await createConnection({
                host: this._env.configHost.db.host,
                user: this._env.configHost.db.usr,
                password: this._env.configHost.db.pwd,
                database: this._env.configHost.db.schema,
                port: this._env.configHost.db.port
            });
        return this._connection;
    }

    public async query<T>(query: string, params: any[] = [])
    {
        const conn = await this.getConnection();
        const [rows] = await conn.execute(query, params);
        return rows as T[];
    }
}

A repository to work must extend the base class Repository, this way the constructor sign is compatible with the automatic initialized and it contains also the App Environment.

Service

Like the repository, the Service is a class that must extends a base class called Service that consent to access to the app environment and the logger.

It contains also another method called: getRepository that return a selected repository instance.

Here the Service example for the authentication:

export class AuthService extends Service
{

    /**
     * NOTE: generate token and refresh token
     * @param user user
     * @param secret secret
     * @returns couple of token
     */
    public tokenGeneration(user: Auth, secret: string): LoginResponse
    {
        const _tokenExpDays: number = this.env.isDev ? 500 : 3;
        const data: IAuthToken = {
            id: user.Id,
            name: user.Name + " " + user.Surname,
            role: user.Role,
            exp: DateTime.now().plus({ days: _tokenExpDays }).toMillis()
        }
        const auth_token = sign(data, secret, { algorithm: "HS512" });
        const rdata: IRefreshToken = {
            data: { id: user.Id, ts: DateTime.now().toMillis() },
            salt: user.SaltRefresh,
            exp: DateTime.now().plus({ days: _tokenExpDays * 10 }).toMillis()
        }
        const refresh_token = sign(rdata, secret, { algorithm: "HS512" });
        return { auth_token, refresh_token };
    }
    /**
     * Check if user and password are correct and return the user
     * @param mail email
     * @param pwd password
     * @returns user or null if not found
     */
    public async login(mail: string, pwd: string): Promise<Auth | null>
    {
        const db = this.env.getRepository<MYSqlRepository>("MYSqlRepository");
        const rows = await db.select<Auth>("Auth", [["Email", "=", mail], ["Password", "=", pwd]]);
        this.log.debug(JSON.stringify(rows));
        if (rows.length > 0) return rows[0];
        return null;
    }
    /**
     * return user by id
     * @param id 
     * @returns user or null if not found
     */
    public async getAuth(id: number): Promise<Auth | null>
    {
        const db = this.env.getRepository<MYSqlRepository>("MYSqlRepository");
        const rows = await db.select<Auth>("Auth", ["Id", "=", id]);
        if (rows.length > 0) return rows[0];
        return null;
    }
    /**
     * return user by email
     * @param mail email
     * @returns user or null if not found
     */
    public async getAuthByEmail(mail: string): Promise<Auth | null>
    {
        const db = this.env.getRepository<MYSqlRepository>("MYSqlRepository");
        const rows = await db.select<Auth>("Auth", ["Email", "=", mail]);
        if (rows.length > 0) return rows[0];
        return null;
    }
    /**
     * update user password
     * @param id  id of user
     * @param pwd  new password
     * @returns  true if ok, false if not
     */
    public async updatePassword(id: number, pwd: string): Promise<boolean | null>
    {
        const db = this.env.getRepository<MYSqlRepository>("MYSqlRepository");
        const response = await db.update<Auth>([["Password", pwd]], ["Id", "=", id], "Auth");
        if (response)
        {
            return true;
        }
        return false;
    }
    /**
     * add new user
     * @param user
     * @returns id of new user or null if not found
     */
    public async createUser(user: Auth): Promise<number | null>
    {
        const db = this.env.getRepository<MYSqlRepository>("MYSqlRepository");
        const response = await db.insert<Auth>(user, "Auth");
        if (response)
        {
            return response;
        }
        return null;
    }
}

You can see that the service use the MYSqlRepositry via the getRepository function:

    const db = this.env.getRepository<MYSqlRepository>("MYSqlRepository");
    const rows = await db.select<Auth>("Auth", ["Id", "=", id]);
   

and then the service use the repository to execute a query on the database.

NOTE: It's a best practice to follow the file naming rules even for services.

Clone this wiki locally