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

node-springboot

v1.2.0

Published

A Node.js framework that mimics Spring Boot's structure using TypeScript decorators. Features JPA-like ORM, dependency injection, REST controllers, relationship mappings, rate limiting, and security features.

Readme

node-springboot

A powerful Node.js framework that mimics Spring Boot's structure using TypeScript decorators. Build REST APIs with JPA-like ORM, dependency injection, relationship mappings, rate limiting, and comprehensive security features.

Features

  • 🎯 JPA-like ORM: Entity management with automatic table creation and migrations
  • 🔗 Relationship Mappings: OneToOne, OneToMany, ManyToOne, ManyToMany with cascade operations
  • 💉 Dependency Injection: Spring-like @Service, @Repository, @Controller, @Autowired
  • 🚀 REST Controllers: Automatic route registration with decorators
  • 🔒 Security: SQL injection prevention, XSS protection, rate limiting, security headers
  • Performance: Optimized batch loading with JOINs (no N+1 queries)
  • 📊 Pagination & Sorting: Built-in support for paginated queries
  • 🔄 Serialization: Spring Boot-style @JsonManagedReference and @JsonBackReference
  • 📝 TypeScript: Full type safety with decorators and metadata
  • 🔍 Spring Data JPA Features: Method name-based query derivation, @Query, @Modifying, @Param, batch operations

Installation

npm install node-springboot

Quick Start

1. Create an Entity

import { Entity, Table, Column, Id, GeneratedValue } from 'node-springboot';

@Entity()
@Table('users')
export class User {
  @Id
  @GeneratedValue({ strategy: 'AUTO' })
  @Column({ name: 'id', type: 'INT' })
  id?: number;

  @Column({ name: 'username', type: 'VARCHAR(100)', nullable: false })
  username!: string;

  @Column({ name: 'email', type: 'VARCHAR(255)', nullable: false })
  email!: string;
}

2. Create a Repository

import { Repository, JpaRepository, EntityManager, Query, Param } from 'node-springboot';
import { User } from './entities/User';

@Repository
export class UserRepository extends JpaRepository<User, number> {
  constructor(entityManager: EntityManager) {
    super(entityManager, User);
  }

  // Method name-based queries (automatic - no implementation needed!)
  async findByEmail(email: string): Promise<User | null>;
  async findByUsernameAndPassword(username: string, password: string): Promise<User | null>;
  async findByAgeGreaterThan(age: number): Promise<User[]>;
  async countByStatus(status: string): Promise<number>;
  async existsByEmail(email: string): Promise<boolean>;
  async deleteByStatus(status: string): Promise<number>;

  // Custom queries with @Query
  @Query("SELECT * FROM users WHERE email = :email AND status = :status", { nativeQuery: true })
  async findByEmailAndStatus(
    @Param('email') email: string,
    @Param('status') status: string
  ): Promise<User[]>;
}

3. Create a Service

import { Service, Autowired } from 'node-springboot';
import { UserRepository } from './repositories/UserRepository';
import { User } from './entities/User';

@Service
export class UserService {
  @Autowired
  private userRepository!: UserRepository;

  async findAll(): Promise<User[]> {
    return this.userRepository.findAll();
  }

  async findById(id: number): Promise<User | null> {
    return this.userRepository.findById(id);
  }

  async create(user: User): Promise<User> {
    return this.userRepository.save(user);
  }
}

4. Create a REST Controller

import {
  RestController,
  Get,
  Post,
  Put,
  Delete,
  RequestBody,
  PathVariable,
  Autowired
} from 'node-springboot';
import { UserService } from './services/UserService';
import { User } from './entities/User';

@RestController({ path: '/api/users' })
export class UserController {
  @Autowired
  private userService!: UserService;

  @Get()
  async getAllUsers(): Promise<User[]> {
    return this.userService.findAll();
  }

  @Get('/:id')
  async getUserById(@PathVariable('id') id: number): Promise<User | null> {
    return this.userService.findById(id);
  }

  @Post()
  async createUser(@RequestBody() user: User): Promise<User> {
    return this.userService.create(user);
  }

  @Put('/:id')
  async updateUser(
    @PathVariable('id') id: number,
    @RequestBody() user: Partial<User>
  ): Promise<User | null> {
    return this.userService.update(id, user);
  }

  @Delete('/:id')
  async deleteUser(@PathVariable('id') id: number): Promise<{ success: boolean }> {
    const result = await this.userService.delete(id);
    return { success: result };
  }
}

5. Bootstrap Your Application

import 'reflect-metadata';
import { Application } from 'node-springboot';
import { User } from './entities/User';
import { UserRepository } from './repositories/UserRepository';
import { UserService } from './services/UserService';
import { UserController } from './controllers/UserController';

async function bootstrap() {
  const app = new Application({
    port: 3000,
    database: {
      host: process.env.DB_HOST || 'localhost',
      port: parseInt(process.env.DB_PORT || '3306'),
      user: process.env.DB_USER || 'root',
      password: process.env.DB_PASSWORD || '',
      database: process.env.DB_NAME || 'myapp_db'
    },
    rateLimit: {
      windowMs: 60000,      // 1 minute
      maxRequests: 100      // 100 requests per minute
    }
  });

  // Initialize application
  await app.initialize();

  // Register entities
  await app.registerEntities([User]);

  // Register components
  app.registerComponents([
    UserRepository,
    UserService,
    UserController
  ]);

  // Register routes
  app.registerRoutes();

  // Start server
  await app.start();
}

bootstrap();

Relationship Mappings

ManyToOne

@Entity()
@Table('orders')
export class Order {
  @Id
  @GeneratedValue({ strategy: 'AUTO' })
  @Column({ name: 'id', type: 'INT' })
  id?: number;

  @ManyToOne({ targetEntity: () => User, fetch: FetchType.EAGER })
  @JoinColumn({ name: 'user_id' })
  user!: User;
}

OneToMany

@Entity()
@Table('orders')
export class Order {
  @OneToMany({
    targetEntity: () => OrderItem,
    mappedBy: 'order',
    cascade: [CascadeType.ALL],
    fetch: FetchType.EAGER
  })
  @JsonManagedReference()
  items!: OrderItem[];
}

ManyToMany

@Entity()
@Table('students')
export class Student {
  @ManyToMany({
    targetEntity: () => Course,
    fetch: FetchType.EAGER,
    cascade: [CascadeType.ALL]
  })
  @JoinTable({
    name: 'student_courses',
    joinColumn: { name: 'student_id', referencedColumnName: 'id' },
    inverseJoinColumn: { name: 'course_id', referencedColumnName: 'id' }
  })
  @JsonManagedReference()
  courses!: Course[];
}

Pagination & Sorting

// In your controller
@Get()
async getUsers(
  @RequestParam('page') page?: string,
  @RequestParam('size') size?: string,
  @RequestParam('sort') sort?: string,
  @RequestParam('direction') direction?: string
): Promise<User[] | Page<User>> {
  if (page !== undefined || size !== undefined) {
    const pageable = createPageable(page, size, sort, direction);
    return this.userService.findAll(pageable);
  }
  return this.userService.findAll();
}

Rate Limiting

Rate limiting is enabled by default (100 requests per minute). Configure via:

const app = new Application({
  // ...
  rateLimit: {
    windowMs: 60000,        // Time window in milliseconds
    maxRequests: 100,       // Max requests per window
    message: 'Too many requests, please try again later.'
  }
});

Or via environment variables:

RATE_LIMIT_ENABLED=true
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX_REQUESTS=100

Security Features

  • ✅ SQL Injection Prevention (parameterized queries)
  • ✅ XSS Protection (input sanitization)
  • ✅ DoS Protection (request size limits, rate limiting)
  • ✅ Security Headers (X-Frame-Options, X-Content-Type-Options, X-XSS-Protection)
  • ✅ Path Traversal Prevention
  • ✅ Input Validation & Sanitization

API Documentation

Decorators

Entity Decorators:

  • @Entity() - Mark class as entity
  • @Table(name) - Specify table name
  • @Column(options) - Define column
  • @Id - Mark as primary key
  • @GeneratedValue(options) - Auto-increment strategy

Spring Decorators:

  • @Service - Mark as service
  • @Repository - Mark as repository
  • @Controller - Mark as controller
  • @RestController({ path }) - REST controller with base path
  • @Autowired - Dependency injection
  • @Get(path), @Post(path), @Put(path), @Delete(path) - HTTP methods
  • @RequestBody, @PathVariable(name), @RequestParam(name) - Parameter injection

Relationship Decorators:

  • @OneToOne, @OneToMany, @ManyToOne, @ManyToMany - Relationship types
  • @JoinColumn(options) - Foreign key column
  • @JoinTable(options) - Join table for ManyToMany

Serialization Decorators:

  • @JsonManagedReference() - Forward reference (serialized)
  • @JsonBackReference() - Back reference (excluded)
  • @JsonIgnore() - Exclude from serialization

Query Decorators:

  • @Query(value, options?) - Custom SQL/JPQL query
  • @Modifying - Mark query as modifying (update/delete)
  • @Param(name) - Named parameter in queries

Spring Data JPA Features

This framework now supports Spring Data JPA-style method name-based query derivation! No need to write SQL for common queries.

Method Name-Based Queries

Just declare the method - the framework generates the SQL automatically:

@Repository
export class UserRepository extends JpaRepository<User, number> {
  // No implementation needed!
  async findByEmail(email: string): Promise<User | null>;
  async findByUsernameAndPassword(username: string, password: string): Promise<User | null>;
  async findByAgeGreaterThan(age: number): Promise<User[]>;
  async findByStatusIn(statuses: string[]): Promise<User[]>;
  async findFirst10ByOrderByCreatedAtDesc(): Promise<User[]>;
  async countByStatus(status: string): Promise<number>;
  async existsByEmail(email: string): Promise<boolean>;
  async deleteByStatus(status: string): Promise<number>;
}

Supported Query Keywords

  • Logical: And, Or
  • Comparison: GreaterThan, LessThan, GreaterThanEqual, LessThanEqual, Equals, NotEquals
  • String: Like, Containing, StartingWith, EndingWith, IgnoreCase
  • Collection: In, NotIn
  • Null: IsNull, IsNotNull
  • Range: Between
  • Modifiers: First/Top, Distinct, OrderBy, Asc/Desc

Custom Queries

Use @Query for complex queries:

@Query("SELECT * FROM users WHERE email = :email", { nativeQuery: true })
async findByEmail(@Param('email') email: string): Promise<User[]>;

@Modifying
@Query("UPDATE users SET status = ?1 WHERE id = ?2", { nativeQuery: true })
async updateStatus(status: string, id: number): Promise<number>;

Batch Operations

await userRepository.saveAll([user1, user2, user3]);
await userRepository.findAllById([1, 2, 3]);
await userRepository.deleteAll();
await userRepository.deleteAllById([1, 2, 3]);

See SPRING_DATA_JPA_FEATURES.md for complete documentation.

Examples

See the examples directory for complete working examples.

Requirements

  • Node.js >= 14.0.0
  • TypeScript >= 4.0.0
  • MySQL 5.7+ or MariaDB

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues, questions, or contributions, please open an issue on GitHub.