Hello everyone,

It’s been a while since my last update in The 10-Minute Company series. I originally thought I’d be able to share progress sooner, but the holiday season (and a few unexpected projects) absorbed much of my free time. Nevertheless, I made some good progress—both on Fidely and on a smaller side project that popped up in early January.

A Quick Detour: Building a Landing Page with Next.js

During the first two weeks of January, I helped a friend who offers online consultations by building a simple landing page for her services. The project was straightforward but fun, and it gave me a chance to work with:

  • Next.js for the frontend,

  • Brevo (formerly Sendinblue) for handling newsletter subscriptions and automated emails,

  • Brevo Meeting for scheduling online consultations,

  • Clerk.com for authentication and an admin dashboard,

  • Stripe for handling payments and checkout.

One of the most interesting parts of the project was setting up an automated workflow: when someone subscribes to the newsletter and completes the double opt-in, they receive a welcome coupon code via email. This ensures only verified users get access to the special offer, adding an extra layer of engagement and trust.

Now, back to Fidely.

Returning to Fidely: Cleaning Up the Backend

With the side project wrapped up, I finally got back to working on Fidely. The focus of the last few days has been restructuring the backend to improve maintainability and scalability. Here’s what I worked on:

1. Refactoring API Routes

I split API endpoints more logically, ensuring that different resources and functionalities were correctly separated. This cleanup makes the backend easier to navigate and will help with future expansions.

Here’s a quick look at the updated routing system:

import { Router } from "express";
import authenticate from "../../middlewares/authenticate";
import authRoutes from "./auth";
import companiesRoutes from "./companies";
import campaignsRoutes from "./campaigns";
import cardCampaignsRoutes from "./card-campaigns";
import cardsRoutes from "./cards";
import transactionsRoutes from "./transactions";
import campaignBuildersRoutes from "./campaign-builders";

const v1Routes = Router();

// Public routes (no auth required)
v1Routes.use("/auth", authRoutes);

// Protected routes
v1Routes.use("/companies", authenticate, companiesRoutes);
v1Routes.use("/campaigns", authenticate, campaignsRoutes);
v1Routes.use("/card-campaigns", authenticate, cardCampaignsRoutes);
v1Routes.use("/cards", authenticate, cardsRoutes);
v1Routes.use("/transactions", authenticate, transactionsRoutes);
v1Routes.use("/campaign-builders", authenticate, campaignBuildersRoutes);

export default v1Routes;

2. Creating a Simple Seeding System

Manually setting up test data was becoming a hassle, so I implemented an automated seeding script that:

  • Clears the database,

  • Inserts fake data for testing and demo purposes.

Here’s the updated seed system:

import { SeedContext } from './types';
import { seedUsers } from './users';
import { seedCompanies } from './companies';
import { seedCampaigns } from './campaigns';
import { seedCards } from './cards';

async function main() {
  const context: SeedContext = {
    users: {},
    companies: {},
    campaigns: {},
    cards: {},
    catalogs: {}
  };

  try {
    console.log('🌱 Starting seed...');

    // Seed users
    console.log('Seeding users...');
    await seedUsers(context);

    // Seed companies
    console.log('Seeding companies...');
    await seedCompanies(context);

    // Seed campaigns
    console.log('Seeding campaigns...');
    await seedCampaigns(context);

    // Seed cards
    console.log('Seeding cards...');
    await seedCards(context);

    console.log('✅ Seed completed successfully!');
  } catch (error) {
    console.error('❌ Seed failed:', error);
    process.exit(1);
  }
}

main();

One of the key advantages of using SQLite for both seeding and testing is the ability to spin up different databases for development and testing. This also allows for testing database migrations with Drizzle in a controlled environment.

3. Introducing Automated Testing

Fidely now has its first batch of automated tests! I’ve added both:

  • Unit tests for core logic,

  • End-to-end (E2E) tests using Mocha and SuperAgent.

This is just the beginning, but having a testing framework in place will make future development smoother and more reliable.

Struggles with Monorepos

One of the biggest challenges I’ve encountered (and still haven’t solved) is setting up a monorepo for both frontend and backend. Since the whole project is in TypeScript, a monorepo could help share code between them.

Yesterday, I spent several hours trying to migrate to pnpm workspaces, but I ran into issues with better-sqlite3 during compilation. The new setup fails to find the compiled drivers—even after trying a postinstall recompilation, nothing changed. 😫

I haven’t figured out a solution yet, and for now, I don’t want to get stuck on this problem. If anyone has dealt with similar issues, I’d love to hear your thoughts! 🤔

Next Steps

I’m really happy with the backend refactor, and my next focus will be the frontend. The plan for the upcoming weeks includes:

  • Enhancing the UI/UX,

  • Continuing to add automated tests,

  • Abstracting the transaction management system to allow for different transaction sources (currently, only operators can generate transactions).

The overall goal remains the same: keeping the codebase clean and flexible so that Fidely can evolve naturally.

That’s it for this update! If you’ve worked on similar refactors, monorepos, or pnpm workspace issues, I’d love to hear your experiences. Let’s chat in the comments or via email!

Until next time! 🚀