npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

arvox-backend

v1.1.7

Published

Un framework backend moderne et modulaire basé sur Hono, TypeScript et l'architecture hexagonale avec authentification Better Auth + Drizzle intégrée

Readme

arvox-backend

Un framework backend TypeScript moderne basé sur Hono, conçu selon l'architecture hexagonale pour créer des APIs robustes et maintenables avec authentification Better Auth + Drizzle intégrée.

⚡ Installation rapide

Créez un nouveau projet avec le CLI :

npx create-arvox-app init mon-projet
cd mon-projet
npm run dev

Ou pour le développement local :

# Clone le framework
git clone <repo-url>
cd backend-framework

# Créer un nouveau projet
node bin/create-arvox-app.js init mon-projet
cd mon-projet
# Modifier package.json pour pointer vers le framework local
npm install
npm run dev

🔐 Authentification Better Auth + Drizzle

Le framework inclut maintenant un générateur d'authentification intégré utilisant Better Auth et Drizzle ORM :

🚀 Génération rapide

# Générer l'authentification complète
npx arvox-auth generate --social github,google

# Générer seulement le schéma Drizzle
npx arvox-auth schema --provider postgresql

# Générer seulement la configuration
npx arvox-auth config --social github,discord

# Valider une configuration existante
npx arvox-auth validate

📁 Fichiers générés dans ./src/db/

db/
├── schema.ts              # Schéma Drizzle (users, sessions, accounts, verifications)
├── index.ts               # Client de base de données
├── auth.config.ts         # Configuration Better Auth
├── integration-example.ts # Exemple d'intégration dans votre app
└── migrations/            # Dossier des migrations

✨ Schéma généré

// db/schema.ts - Tables optimisées pour Better Auth
export const users = pgTable('users', {
  id: text('id').primaryKey(),
  name: text('name').notNull(),
  firstname: text('firstname'),
  lastname: text('lastname'),
  email: text('email').notNull().unique(),
  lastLoginAt: timestamp('last_login_at'),
  emailVerified: boolean('email_verified').notNull(),
  image: text('image'),
  role: text('role').notNull().default('user'),
  isAdmin: boolean('is_admin').notNull().default(false),
  createdAt: timestamp('created_at').notNull(),
  updatedAt: timestamp('updated_at').notNull()
});

export const sessions = pgTable('sessions', {
  id: text('id').primaryKey(),
  expiresAt: timestamp('expires_at').notNull(),
  token: text('token').notNull().unique(),
  userId: text('user_id').notNull().references(() => users.id),
  impersonatedBy: text('impersonated_by').references(() => users.id),
  // ... autres champs
});

🔧 Intégration dans votre application

import { ArvoxFramework, AuthModuleFactory } from 'arvox-backend';
import { db } from './src/db'; // Client généré
import { authConfig } from './src/db/auth.config'; // Configuration générée

// Créer le module d'authentification
const authModule = AuthModuleFactory.create({
  auth: authConfig,
  db: db,
});

const framework = new ArvoxFramework({
  appName: 'Mon API avec Auth',
  version: '1.0.0',
  port: 3000,
});

// Enregistrer l'authentification
framework.registerModule(authModule.module);
framework.registerService(authModule.authService);

// Routes protégées
const app = framework.getApp();
app.get('/api/protected', authModule.middleware.required, (c) => {
  const user = c.get('user');
  return c.json({ message: 'Protected endpoint', user });
});

await framework.start();

🎯 Endpoints d'authentification automatiques

Une fois intégré, votre API dispose automatiquement de :

POST /api/v1/auth/sign-up/email     # Inscription par email
POST /api/v1/auth/sign-in/email     # Connexion par email
GET  /api/v1/auth/me                # Profil utilisateur
POST /api/v1/auth/sign-out          # Déconnexion
GET  /api/v1/auth/session           # Vérifier la session

# Avec providers sociaux configurés :
GET  /api/v1/auth/sign-in/github    # Connexion GitHub
GET  /api/v1/auth/sign-in/google    # Connexion Google

⚙️ Configuration CLI arvox-auth

# Options disponibles
npx arvox-auth generate \
  --provider postgresql \          # ou mysql, sqlite
  --output ./custom-db \          # dossier de sortie (défaut: ./src/db)
  --auth-url http://localhost:3000 \  # URL de base
  --social github,google,discord      # providers sociaux

# Commandes spécialisées
npx arvox-auth init                 # Interface interactive (à venir)
npx arvox-auth schema --provider mysql
npx arvox-auth config --social github
npx arvox-auth validate            # Vérifie la configuration

📦 Installation manuelle

npm install arvox-backend
# ou
bun add arvox-backend
# ou  
pnpm add arvox-backend

🚀 Simplification des routes

Le framework offre 3 approches pour créer des routes avec 60-70% moins de code :

✨ Méthode 1 : BaseController simplifié (Recommandé)

import { BaseController } from 'arvox-backend';

export class UserController extends BaseController {
  constructor() {
    super();
    this.setupRoutes();
  }

  private setupRoutes() {
    // ✅ AVANT : 40+ lignes de createRoute
    // ✅ APRÈS : 8 lignes avec méthodes simplifiées

    this.createPostRoute('/users', {
      body: z.object({
        name: z.string(),
        email: z.string().email()
      }),
      summary: 'Créer un utilisateur'
    }, async (c) => {
      // Votre logique métier ici
      return c.json({ success: true });
    });

    this.createListRoute('/users', {
      summary: 'Liste des utilisateurs',
      query: z.object({
        page: z.string().optional(),
        limit: z.string().optional()
      })
    }, async (c) => {
      // Pagination automatique incluse
      return c.json({ users: [] });
    });

    this.createGetByIdRoute('/users/:id', {
      summary: 'Obtenir un utilisateur par ID'
    }, async (c) => {
      const id = c.req.param('id');
      return c.json({ user: { id } });
    });
  }
}

⚡ Méthode 2 : Utilitaires Route

import { Route } from 'arvox-backend/utils';

// Configuration encore plus simple - style fonctionnel
const userRoutes = [
  Route.post('/users', {
    body: z.object({
      name: z.string(),
      email: z.string().email()
    }),
    summary: 'Créer un utilisateur'
  }, async (c) => {
    return c.json({ success: true });
  }),

  Route.getList('/users', {
    summary: 'Liste des utilisateurs'
  }, async (c) => {
    return c.json({ users: [] });
  }),

  Route.getById('/users/:id', {
    summary: 'Utilisateur par ID'
  }, async (c) => {
    return c.json({ user: { id: c.req.param('id') } });
  })
];

� Méthode 3 : Hybride (Best of Both)

export class UserController extends BaseController {
  constructor() {
    super();
    this.setupRoutes();
  }

  private setupRoutes() {
    // Mélanger les deux approches selon les besoins
    this.createPostRoute('/users', userCreationConfig, this.createUser);
    
    // Ou utiliser les utilitaires directement
    this.addRoute(Route.delete('/users/:id', {
      summary: 'Supprimer un utilisateur'
    }, this.deleteUser));
  }

  private createUser = async (c) => { /* logic */ };
  private deleteUser = async (c) => { /* logic */ };
}

📊 Comparaison des méthodes

| Critère | Méthode 1 (BaseController) | Méthode 2 (Route Utils) | Méthode 3 (Hybride) | |---------|---------------------------|------------------------|---------------------| | Réduction de code | ~60% | ~70% | ~65% | | Lisibilité | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | | Flexibilité | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | | Courbe d'apprentissage | Facile | Très facile | Modérée |

�🏗️ Architecture

Le framework suit l'architecture hexagonale :

src/
├── controllers/     # 🎮 Couche présentation (HTTP)
├── services/        # 💼 Couche application (logique métier)
├── repositories/    # 🗄️ Couche infrastructure (données)
├── use-cases/       # 🎯 Couche domaine (règles métier)
└── types/          # 📝 Types partagés

� Fonctionnalités

  • Architecture hexagonale prête à l'emploi
  • Validation Zod automatique
  • Documentation OpenAPI générée automatiquement
  • Pagination intégrée
  • Gestion d'erreurs standardisée
  • CLI de scaffolding pour nouveaux projets
  • TypeScript support complet
  • Performance optimisée avec Hono

�📚 Guide de démarrage

  1. Créer un projet : npx create-arvox-app init mon-api
  2. Choisir votre approche : BaseController, Route Utils, ou Hybride
  3. Développer : npm run dev
  4. Tester : Visitez /health et /doc
  5. Déployer : npm run build && npm start

📖 Exemples

Consultez le dossier examples/ pour voir des implémentations complètes :

🔧 CLI Tools

Le framework Arvox fournit deux CLI pour faciliter le développement :

📦 create-arvox-app - Générateur de projets

# Nouveau projet avec npm
npx create-arvox-app init mon-projet

# Avec bun
npx create-arvox-app init mon-projet --package-manager bun

# Avec pnpm  
npx create-arvox-app init mon-projet --package-manager pnpm

Génère :

  • 📁 Structure de dossiers optimisée
  • 🏥 Contrôleur Health avec endpoint /health
  • 📖 Documentation automatique sur /doc
  • ⚙️ Configuration TypeScript/ESLint
  • 🔥 Hot reload avec tsx

🔐 arvox-auth - Générateur d'authentification

# Génération complète (recommandé)
npx arvox-auth generate --social github,google

# Options avancées
npx arvox-auth generate \
  --provider postgresql \
  --output ./src/db \
  --auth-url http://localhost:3000 \
  --social github,google,discord

# Commandes spécialisées
npx arvox-auth schema --provider mysql     # Schéma seulement
npx arvox-auth config --social github      # Config seulement
npx arvox-auth validate                    # Validation

Génère :

  • 🗄️ Schéma Drizzle optimisé (users, sessions, accounts, verifications)
  • ⚙️ Configuration Better Auth avec providers sociaux
  • 📝 Fichier .env.example avec toutes les variables
  • 🔗 Client de base de données typé
  • 📋 Exemple d'intégration complet
  • 🛠️ Scripts de migration Drizzle

🎯 Workflow recommandé

# 1. Créer le projet
npx create-arvox-app init mon-api-auth
cd mon-api-auth

# 2. Générer l'authentification  
npx arvox-auth generate --social github,google

# 3. Installer les dépendances auth
npm install better-auth drizzle-orm postgres

# 4. Configurer .env (copier depuis .env.example)
cp .env.example .env
# Éditer .env avec vos vraies valeurs

# 5. Lancer en développement
npm run dev

# 6. Tester les endpoints
curl http://localhost:3000/api/v1/auth/me
curl http://localhost:3000/docs  # Documentation

🎯 Objectif : Réduire la verbosité du code de 60-70% tout en gardant la flexibilité et la robustesse.

🚀 Fonctionnalités

  • Architecture Hexagonale : Séparation claire entre Domain, Application et Infrastructure
  • TypeScript First : Type safety complet avec Zod pour la validation
  • OpenAPI/Swagger : Documentation API automatique
  • Modulaire : Système de modules et services extensible
  • Hono Framework : Performance optimale avec support moderne
  • Classes de Base : UseCase, Controller, Repository, Service prêts à l'emploi
  • Utilitaires : Pagination, validation, gestion des réponses standardisées

📦 Installation

npm install arvox-backend
# ou
bun add arvox-backend

🏗️ Architecture

src/
├── domain/           # Couche métier (entités, interfaces, types)
├── application/      # Couche application (cas d'usage, services)
└── infrastructure/   # Couche infrastructure (controllers, DB, APIs externes)

🚀 Démarrage Rapide

1. Configuration de Base

import { ArvoxFramework } from 'arvox-backend'

const framework = new ArvoxFramework({
  appName: 'Mon API',
  version: '1.0.0',
  port: 3000,
  environment: 'development',
  cors: {
    origin: '*',
    methods: ['GET', 'POST', 'PUT', 'DELETE'],
    headers: ['Content-Type', 'Authorization']
  },
  logging: {
    requests: true,
    errors: true
  }
})

// Démarrer l'application
await framework.start()

2. Créer un Use Case

import { BaseUseCase, ActivityType } from 'arvox-backend'

type CreateUserParams = {
  name: string
  email: string
}

type CreateUserResponse = {
  success: boolean
  data?: User
  error?: string
}

export class CreateUserUseCase extends BaseUseCase<CreateUserParams, CreateUserResponse> {
  constructor(private userRepository: UserRepository) {
    super()
  }

  async execute(params: CreateUserParams): Promise<CreateUserResponse> {
    try {
      const user = await this.userRepository.create(params)
      return this.createSuccessResponse(user)
    } catch (error) {
      return this.handleError(error)
    }
  }

  log(): ActivityType {
    return ActivityType.CREATE_USER
  }
}

3. Créer un Controller

import { BaseController, Route } from 'arvox-backend'
import { z } from 'zod'

// Schémas simplifiés
const CreateUserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email()
})

const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string()
})

export class UserController extends BaseController {
  constructor(private createUserUseCase: CreateUserUseCase) {
    super()
  }

  initRoutes() {
    // ✨ APPROCHE SIMPLIFIÉE - 70% moins de code !
    
    // POST /users avec createPostRoute (méthode BaseController)
    this.createPostRoute(
      '/users',
      {
        request: CreateUserSchema,
        response: UserSchema,
        summary: 'Create user',
        description: 'Create a new user in the system'
      },
      async (c, body) => {
        const result = await this.createUserUseCase.execute(body)
        return result.success 
          ? c.json(result, 201)
          : c.json(result, 400)
      },
      { security: true } // Options : sécurité, multipart, etc.
    )

    // Alternative avec Route utility
    this.controller.openapi(
      Route.post('/users-alt', {
        request: CreateUserSchema,
        response: UserSchema
      }, {
        tag: 'Users',
        summary: 'Create user (alternative)',
        security: true
      }),
      async (c) => {
        const body = c.req.valid('json')
        const result = await this.createUserUseCase.execute(body)
        return result.success 
          ? c.json(result, 201)
          : c.json(result, 400)
      }
    )

    // GET /users avec pagination automatique
    this.createListRoute(
      '/users',
      {
        response: UserSchema,
        summary: 'Get users list',
        description: 'Retrieve paginated list of users'
      },
      async (c, query) => {
        // query contient automatiquement : { page, limit, search, sort }
        const users = [] // Votre logique ici
        const total = 0
        
        return c.json({
          success: true,
          data: {
            items: users,
            pagination: {
              total, page: query.page, limit: query.limit,
              totalPages: Math.ceil(total / query.limit),
              hasNext: query.page < Math.ceil(total / query.limit),
              hasPrev: query.page > 1
            }
          }
        })
      },
      { security: true }
    )

    // GET /users/{id}
    this.createGetByIdRoute(
      '/users/{id}',
      {
        response: UserSchema,
        summary: 'Get user by ID'
      },
      async (c, id) => {
        const user = null // Votre logique de récupération
        return user 
          ? c.json({ success: true, data: user })
          : c.json({ success: false, error: 'User not found' }, 404)
      },
      { security: true }
    )

    // PUT /users/{id}
    this.createPutRoute(
      '/users/{id}',
      {
        request: CreateUserSchema.partial(), // Schema de mise à jour
        response: UserSchema,
        summary: 'Update user'
      },
      async (c, id, body) => {
        const updatedUser = null // Votre logique de mise à jour
        return updatedUser
          ? c.json({ success: true, data: updatedUser })
          : c.json({ success: false, error: 'User not found' }, 404)
      },
      { security: true }
    )

    // DELETE /users/{id}
    this.createDeleteRoute(
      '/users/{id}',
      {
        summary: 'Delete user'
      },
      async (c, id) => {
        const deleted = true // Votre logique de suppression
        return deleted
          ? c.json({ success: true, data: { deleted: true } })
          : c.json({ success: false, error: 'User not found' }, 404)
      },
      { security: true }
    )
  }
}

4. Créer un Repository

import { BaseRepository } from 'arvox-backend'

export class UserRepository extends BaseRepository<User, CreateUserData, UpdateUserData> {
  async findById(id: string): Promise<User | null> {
    // Implémentation avec votre ORM (Drizzle, Prisma, etc.)
  }

  async findAll(pagination?: { skip: number; limit: number }): Promise<User[]> {
    // Implémentation avec pagination
  }

  async create(data: CreateUserData): Promise<User> {
    // Implémentation de création
  }

  async update(id: string, data: UpdateUserData): Promise<User> {
    // Implémentation de mise à jour
  }

  async delete(id: string): Promise<boolean> {
    // Implémentation de suppression
  }

  async count(): Promise<number> {
    // Implémentation du comptage
  }
}

5. Créer un Module

import { IModule } from 'arvox-backend'
import { OpenAPIHono } from '@hono/zod-openapi'

export class UserModule implements IModule {
  constructor(
    private userController: UserController
  ) {}

  getName(): string {
    return 'UserModule'
  }

  async initialize(): Promise<void> {
    console.log('User module initialized')
  }

  registerRoutes(app: OpenAPIHono): void {
    app.route('/api/v1', this.userController.controller)
  }

  async cleanup(): Promise<void> {
    console.log('User module cleaned up')
  }
}

6. Assemblage Final

import { ArvoxFramework } from 'arvox-backend'

// Injection de dépendances
const userRepository = new UserRepository()
const createUserUseCase = new CreateUserUseCase(userRepository)
const userController = new UserController(createUserUseCase)
const userModule = new UserModule(userController)

// Configuration du framework
const framework = new ArvoxFramework({
  appName: 'Mon API',
  version: '1.0.0',
  port: 3000
})

// Enregistrement du module
framework.registerModule(userModule)

// Démarrage
await framework.start()

🛠️ Utilitaires Inclus

Simplification des Routes

Le framework offre plusieurs approches pour simplifier la création de routes OpenAPI :

🚀 Méthodes BaseController (Recommandé)

// Au lieu de 40+ lignes avec createRoute, utilisez :
this.createPostRoute('/users', {
  request: CreateUserSchema,
  response: UserSchema,
  summary: 'Create user'
}, async (c, body) => {
  // Votre logique ici
}, { security: true })

// Autres méthodes disponibles :
this.createListRoute()      // GET avec pagination
this.createGetByIdRoute()   // GET /{id}
this.createPutRoute()       // PUT /{id}
this.createDeleteRoute()    // DELETE /{id}

⚡ Route Utilities

import { Route } from '@arvox/backend-framework'

// Pour plus de flexibilité :
this.controller.openapi(
  Route.post('/users', {
    request: CreateUserSchema,
    response: UserSchema
  }, {
    tag: 'Users',
    summary: 'Create user',
    security: true
  }),
  async (c) => {
    // Votre handler ici
  }
)

📊 Avantages de la Simplification

| Approche | Lignes de Code | Avantages | |----------|----------------|-----------| | createRoute original | ~40 lignes | Contrôle total | | Méthodes BaseController | ~15 lignes | -60% code, type safety | | Route utilities | ~12 lignes | -70% code, réutilisable |

Fonctionnalités automatiques :

  • ✅ Schémas de réponse standardisés
  • ✅ Gestion d'erreurs communes (400, 401, 404)
  • ✅ Documentation OpenAPI complète
  • ✅ Validation automatique des paramètres
  • ✅ Type safety complet avec TypeScript

Validation

import { ValidationUtil } from 'arvox-backend'

// Schémas prédéfinis
const user = ValidationUtil.validate(data, z.object({
  email: ValidationUtil.emailSchema,
  password: ValidationUtil.passwordSchema
}))

// Validation de fichiers
const fileSchema = ValidationUtil.createFileSchema(['image/jpeg', 'image/png'], 5 * 1024 * 1024)

Pagination

import { PaginationUtil } from 'arvox-backend'

const paginationUtil = new PaginationUtil()
const { page, limit, skip } = paginationUtil.extractFromContext(c)
const result = paginationUtil.createResponse(items, total, page, limit)

Réponses Standardisées

import { ResponseUtil } from 'arvox-backend'

const responseUtil = new ResponseUtil()

// Réponses de succès
return responseUtil.success(data)
return responseUtil.paginated(items, total, page, limit)

// Réponses d'erreur
return responseUtil.error('Message d\'erreur')
return responseUtil.notFound('User')
return responseUtil.unauthorized()

📚 Documentation API

Une fois votre application démarrée, la documentation Swagger est automatiquement disponible sur :

  • Documentation interactive : http://localhost:3000/docs
  • Spécification OpenAPI : http://localhost:3000/openapi.json

🔧 Configuration Avancée

const framework = new ArvoxFramework({
  appName: 'Mon API',
  version: '1.0.0',
  port: 3000,
  environment: 'production',
  
  // Configuration CORS
  cors: {
    origin: ['https://monsite.com'],
    methods: ['GET', 'POST', 'PUT', 'DELETE'],
    headers: ['Content-Type', 'Authorization']
  },
  
  // Configuration des logs
  logging: {
    requests: true,
    errors: true
  },
  
  // Configuration de sécurité
  security: {
    headers: true
  }
})

🧪 Tests

Le framework inclut des classes de base qui facilitent les tests unitaires :

import { beforeEach, describe, expect, it, vi } from 'vitest'

describe('CreateUserUseCase', () => {
  let useCase: CreateUserUseCase
  let mockRepository: UserRepository

  beforeEach(() => {
    mockRepository = {
      create: vi.fn()
    } as any
    useCase = new CreateUserUseCase(mockRepository)
  })

  it('should create user successfully', async () => {
    const userData = { name: 'John', email: '[email protected]' }
    const mockUser = { id: '1', ...userData }
    
    vi.mocked(mockRepository.create).mockResolvedValue(mockUser)

    const result = await useCase.execute(userData)

    expect(result.success).toBe(true)
    expect(result.data).toEqual(mockUser)
  })
})

🤝 Contribution

Les contributions sont les bienvenues ! Merci de suivre les guidelines du projet.

📄 Licence

MIT License - voir le fichier LICENSE pour plus de détails.

🆘 Support

Pour toute question ou problème, n'hésitez pas à ouvrir une issue sur GitHub.