
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
node-springboot
Advanced tools
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.
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.
@Service, @Repository, @Controller, @Autowired@JsonManagedReference and @JsonBackReference@Query, @Modifying, @Param, batch operationsnpm install node-springboot
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;
}
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[]>;
}
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);
}
}
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 };
}
}
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();
@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;
}
@Entity()
@Table('orders')
export class Order {
@OneToMany({
targetEntity: () => OrderItem,
mappedBy: 'order',
cascade: [CascadeType.ALL],
fetch: FetchType.EAGER
})
@JsonManagedReference()
items!: OrderItem[];
}
@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[];
}
// 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 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
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 strategySpring 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 injectionRelationship Decorators:
@OneToOne, @OneToMany, @ManyToOne, @ManyToMany - Relationship types@JoinColumn(options) - Foreign key column@JoinTable(options) - Join table for ManyToManySerialization Decorators:
@JsonManagedReference() - Forward reference (serialized)@JsonBackReference() - Back reference (excluded)@JsonIgnore() - Exclude from serializationQuery Decorators:
@Query(value, options?) - Custom SQL/JPQL query@Modifying - Mark query as modifying (update/delete)@Param(name) - Named parameter in queriesThis framework now supports Spring Data JPA-style method name-based query derivation! No need to write SQL for common 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>;
}
And, OrGreaterThan, LessThan, GreaterThanEqual, LessThanEqual, Equals, NotEqualsLike, Containing, StartingWith, EndingWith, IgnoreCaseIn, NotInIsNull, IsNotNullBetweenFirst/Top, Distinct, OrderBy, Asc/DescUse @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>;
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.
See the examples directory for complete working examples.
MIT
Contributions are welcome! Please feel free to submit a Pull Request.
For issues, questions, or contributions, please open an issue on GitHub.
FAQs
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.
We found that node-springboot demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.