
Research
/Security News
10 npm Typosquatted Packages Deploy Multi-Stage Credential Harvester
Socket researchers found 10 typosquatted npm packages that auto-run on install, show fake CAPTCHAs, fingerprint by IP, and deploy a credential stealer.
OpenAPI v3 compliant REST framework for Node.js, with support for MongoDB and JSON-Schema
A powerful OpenAPI v3 compliant REST framework for Node.js with comprehensive MongoDB support, JSON Schema validation, authentication, and authorization. Build production-ready RESTful APIs in minutes with automatic OpenAPI documentation generation.
# npm
npm install arrest
# pnpm
pnpm add arrest
# yarn
yarn add arrest
import { API, MongoResource } from 'arrest';
const api = new API({
info: {
title: 'My API',
version: '1.0.0'
}
});
// Add a MongoDB-backed resource
api.addResource(new MongoResource('mongodb://localhost:27017/mydb', {
name: 'User',
collection: 'users'
}));
// Start the server
api.listen(3000);
console.log('API running at http://localhost:3000');
console.log('OpenAPI spec at http://localhost:3000/openapi.json');
This creates a full CRUD API for users with the following endpoints:
GET /users - List users with filtering, sorting, paginationPOST /users - Create a new userGET /users/{id} - Get user by IDPUT /users/{id} - Update userPATCH /users/{id} - Partial update user with JSON PatchDELETE /users/{id} - Delete userOnce your API is running, you can interact with it using curl or any HTTP client:
# List all users
curl "http://localhost:3000/users"
# Create a new user
curl "http://localhost:3000/users" \
-H "Content-Type: application/json" \
-X POST \
-d '{"name": "John Doe", "email": "john@example.com"}'
# Get a specific user
curl "http://localhost:3000/users/507f1f77bcf86cd799439011"
# Update a user
curl "http://localhost:3000/users/507f1f77bcf86cd799439011" \
-H "Content-Type: application/json" \
-X PUT \
-d '{"name": "John Smith", "email": "john.smith@example.com"}'
# Delete a user
curl "http://localhost:3000/users/507f1f77bcf86cd799439011" -X DELETE
import { API, Resource, Operation } from 'arrest';
class CustomOperation extends Operation {
constructor(resource, path, method) {
super(resource, path, method, 'customOp');
}
getDefaultInfo() {
return {
summary: 'Custom operation',
description: 'Performs a custom operation',
responses: {
'200': {
description: 'Success',
content: {
'application/json': {
schema: { type: 'object' }
}
}
}
}
};
}
async handler(req, res, next) {
try {
const result = await this.runOperation({ req, res });
res.json(result);
} catch (error) {
next(error);
}
}
async runOperation(job) {
return { message: 'Custom operation executed', timestamp: new Date() };
}
}
const api = new API();
const resource = new Resource({ name: 'Custom' });
resource.addOperation(new CustomOperation(resource, '/action', 'post'));
api.addResource(resource);
arrest follows a three-tier architecture:
import { API, Resource, Operation } from 'arrest';
// 1. Create API instance
const api = new API({
info: {
title: 'My REST API',
version: '1.0.0',
description: 'A comprehensive REST API built with arrest'
}
});
// 2. Create resource with operations
const userResource = new Resource({
name: 'User',
path: 'users' // Optional: defaults to plural of name
});
// 3. Add custom operations to resource
userResource.addOperation('/profile', 'get', async (req, res) => {
res.json({ profile: 'user profile data' });
});
// 4. Add resource to API
api.addResource(userResource);
// 5. Start the server
api.listen(3000);
arrest automatically converts resource names to RESTful paths:
// Resource name -> Path conversion
new Resource({ name: 'User' }); // -> /users
new Resource({ name: 'BlogPost' }); // -> /blog-posts
new Resource({ name: 'UserProfile' }); // -> /user-profiles
// Custom path override
new Resource({
name: 'User',
path: 'customers', // Custom path
namePlural: 'CustomerList' // Custom plural name
});
The main API container that manages resources and server configuration.
Constructor Options:
new API({
info: {
title: 'API Title',
version: '1.0.0',
description: 'API Description'
},
servers: [
{ url: 'https://api.example.com', description: 'Production' },
{ url: 'http://localhost:3000', description: 'Development' }
],
security: [
{ bearerAuth: [] }
]
})
Key Methods:
addResource(resource) - Add a resource to the APIlisten(port, callback?) - Start HTTP serverlisten({ http: 3000, https: 3443, options }) - Start HTTP/HTTPS serversrouter() - Get Express router for integrationattach(app, path?) - Attach to existing Express appRepresents a collection of related operations.
Constructor Options:
new Resource({
name: 'User', // Resource name (required)
path: 'users', // Custom path (optional)
namePlural: 'Users', // Custom plural name (optional)
description: 'User management' // OpenAPI description (optional)
})
Key Methods:
addOperation(path, method, handler) - Add simple operationaddOperation(operationInstance) - Add operation instanceSpecialized resource for MongoDB collections with built-in CRUD operations.
Constructor:
new MongoResource(connectionUri, options, customRoutes?)
Options:
{
name: 'User', // Resource name
collection: 'users', // MongoDB collection name
id: '_id', // ID field name (default: '_id')
idIsObjectId: true, // Whether ID is ObjectId (default: true)
queryLimit: 100, // Maximum query results (default: no limit)
createIndexes: false, // Auto-create indexes (default: false)
escapeProperties: false // Escape MongoDB special characters (default: false)
}
Base class for individual API operations.
Constructor:
new Operation(resource, path, method, operationId)
Key Methods to Override:
getDefaultInfo() - Return OpenAPI operation infohandler(req, res, next) - Express request handlerrunOperation(job) - Main operation logicarrest provides comprehensive input validation using OpenAPI v3 and JSON Schema:
import { Operation } from 'arrest';
class CreateUserOperation extends Operation {
constructor(resource, path, method) {
super(resource, path, method, 'createUser');
}
getDefaultInfo() {
return {
summary: 'Create a new user',
requestBody: {
required: true,
content: {
'application/json': {
schema: {
type: 'object',
required: ['name', 'email'],
additionalProperties: false,
properties: {
name: {
type: 'string',
minLength: 1,
maxLength: 100
},
email: {
type: 'string',
format: 'email'
},
age: {
type: 'integer',
minimum: 0,
maximum: 150
}
}
}
}
}
},
parameters: [
{
name: 'include',
in: 'query',
schema: {
type: 'array',
items: { type: 'string', enum: ['profile', 'preferences'] }
},
style: 'form',
explode: false
}
]
};
}
async runOperation(job) {
const { name, email, age } = job.req.body;
const include = job.req.query.include || [];
// Create user logic here
return { id: '123', name, email, age, created: new Date() };
}
}
arrest supports powerful querying with RQL syntax:
// Examples of RQL queries
const queries = [
// Basic filtering
'eq(status,active)',
'gt(age,18)',
'in(category,electronics,books)',
// Complex queries
'and(eq(status,active),gt(price,100))',
'or(eq(category,sale),lt(price,50))',
// Sorting and pagination
'sort(+name,-created)',
'limit(10,20)', // limit(count, offset)
// Field selection
'select(name,email,created)'
];
// Use in API calls
// GET /users?q=and(eq(status,active),gt(age,18))&sort=-created&limit(10)
import { API, MongoResource } from 'arrest';
class SecureAPI extends API {
initSecurity(req, res, next) {
// Extract and validate OAuth2 token
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'Missing authorization token' });
}
// Validate token and set scopes
req.scopes = ['read:users', 'write:users']; // From token validation
next();
}
}
class SecureOperation extends Operation {
get swaggerScopes() {
return {
'oauth2': ['read:users', 'write:users']
};
}
}
import { defineAbility } from '@casl/ability';
class AuthorizedAPI extends API {
initSecurity(req, res, next) {
// Define user abilities based on their role
req.ability = defineAbility((can, cannot) => {
if (req.user.role === 'admin') {
can('manage', 'all');
} else if (req.user.role === 'user') {
can('read', 'User', { ownerId: req.user.id });
can('update', 'User', { ownerId: req.user.id });
cannot('delete', 'User');
}
});
next();
}
}
import { MongoResource, QueryMongoOperation } from 'arrest';
class AdvancedUserResource extends MongoResource {
constructor() {
super('mongodb://localhost:27017/myapp', {
name: 'User',
collection: 'users',
createIndexes: true
});
}
getIndexes() {
return [
{ key: { email: 1 }, unique: true },
{ key: { 'profile.tags': 1 } },
{ key: { createdAt: -1 } }
];
}
}
// Custom aggregation operation
class UserStatsOperation extends QueryMongoOperation {
async prepareQuery(job) {
// Return MongoDB aggregation pipeline instead of simple query
return [
{ $match: { status: 'active' } },
{ $group: {
_id: '$department',
count: { $sum: 1 },
averageAge: { $avg: '$age' }
}
},
{ $sort: { count: -1 } }
];
}
}
Built-in CSV export functionality:
// GET /users?format=csv&csv_fields=name,email,created
// GET /users?format=csv&csv_fields=name,email&csv_options=header=true&csv_names=Name,Email
arrest supports dual REST and JSON-RPC interfaces:
import { RPCOperation } from 'arrest';
class UserRPCOperation extends RPCOperation {
async getUserProfile(params) {
const { userId } = params;
// Fetch user profile logic
return { profile: { id: userId, name: 'John Doe' } };
}
async updateUserProfile(params) {
const { userId, updates } = params;
// Update logic
return { success: true, updated: updates };
}
}
// JSON-RPC calls:
// POST /users/rpc
// {"jsonrpc": "2.0", "method": "getUserProfile", "params": {"userId": "123"}, "id": 1}
Complex data processing with pipeline support:
import { PipelineOperation } from 'arrest';
class DataProcessingPipeline extends PipelineOperation {
async runOperation(job) {
let data = await super.runOperation(job);
// Apply transformations
data = this.filterSensitiveData(data);
data = this.calculateDerivedFields(data);
data = this.formatForOutput(data, job.req.query.format);
return data;
}
filterSensitiveData(data) {
// Remove sensitive fields based on user permissions
return data.map(item => this.filterFields(item, job.req.ability));
}
}
arrest works seamlessly with existing Express applications:
import express from 'express';
import { API, MongoResource } from 'arrest';
const app = express();
const api = new API();
// Add resources to API
api.addResource(new MongoResource('mongodb://localhost:27017/mydb', {
name: 'User'
}));
// Mount API on Express app
app.use('/api/v1', await api.router());
// Add other Express routes
app.get('/health', (req, res) => {
res.json({ status: 'healthy' });
});
app.listen(3000);
Comprehensive error handling with detailed responses:
import { API } from 'arrest';
class CustomAPI extends API {
handleError(error, req, res, next) {
if (error.name === 'ValidationError') {
return res.status(400).json({
error: 'Validation failed',
details: error.errors,
path: error.path
});
}
if (error.code === 11000) { // MongoDB duplicate key
return res.status(409).json({
error: 'Resource already exists',
field: Object.keys(error.keyPattern)[0]
});
}
// Default error handling
super.handleError(error, req, res, next);
}
}
Full TypeScript definitions are included:
import { API, MongoResource, Operation } from 'arrest';
import { Request, Response } from 'express';
interface User {
id: string;
name: string;
email: string;
createdAt: Date;
}
class TypedUserOperation extends Operation {
async runOperation(job: { req: Request; res: Response }): Promise<User> {
const userData = job.req.body as Partial<User>;
// Implementation with type safety
return {
id: 'generated-id',
name: userData.name!,
email: userData.email!,
createdAt: new Date()
};
}
}
fields parameterimport { API, MongoResource } from 'arrest';
const api = new API({
info: { title: 'Production API', version: '1.0.0' }
});
// Production MongoDB resource with optimization
api.addResource(new MongoResource('mongodb://mongo-cluster/prod-db', {
name: 'User',
collection: 'users',
queryLimit: 100, // Limit results
createIndexes: true, // Auto-create indexes
escapeProperties: true // Security: escape special chars
}));
// Start with both HTTP and HTTPS
api.listen({
http: 8080,
https: 8443,
httpsOptions: {
key: fs.readFileSync('private-key.pem'),
cert: fs.readFileSync('certificate.pem')
}
});
MIT License - see the LICENSE file for details.
Contributions are welcome! Please ensure all tests pass:
pnpm install
pnpm test
pnpm run coverage
FAQs
OpenAPI v3 compliant REST framework for Node.js, with support for MongoDB and JSON-Schema
The npm package arrest receives a total of 1,462 weekly downloads. As such, arrest popularity was classified as popular.
We found that arrest demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Β It has 4 open source maintainers 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.

Research
/Security News
Socket researchers found 10 typosquatted npm packages that auto-run on install, show fake CAPTCHAs, fingerprint by IP, and deploy a credential stealer.

Product
Socket Firewall Enterprise is now available with flexible deployment, configurable policies, and expanded language support.

Security News
Open source dashboard CNAPulse tracks CVE Numbering Authoritiesβ publishing activity, highlighting trends and transparency across the CVE ecosystem.