express-nodejs-expert

Express.js & Node.js Expert

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "express-nodejs-expert" with this command: npx skills add webdev70/hosting-google/webdev70-hosting-google-express-nodejs-expert

Express.js & Node.js Expert

This skill provides comprehensive expert knowledge of Express.js web framework and Node.js runtime for building robust, secure, and performant web applications and API servers.

Express Application Structure

Basic Application Setup

Minimal Express app:

const express = require('express'); const app = express(); const port = process.env.PORT || 3000;

app.get('/', (req, res) => { res.send('Hello World!'); });

app.listen(port, () => { console.log(Server running at http://localhost:${port}); });

Production-ready structure:

const express = require('express'); const helmet = require('helmet'); const cors = require('cors'); const morgan = require('morgan');

const app = express(); const port = process.env.PORT || 3000;

// Middleware app.use(helmet()); // Security headers app.use(cors()); // CORS support app.use(morgan('combined')); // Logging app.use(express.json()); // Parse JSON bodies app.use(express.urlencoded({ extended: true })); // Parse URL-encoded bodies app.use(express.static('public')); // Serve static files

// Routes app.use('/api', require('./routes/api'));

// Error handling app.use((err, req, res, next) => { console.error(err.stack); res.status(err.status || 500).json({ error: { message: err.message, ...(process.env.NODE_ENV === 'development' && { stack: err.stack }) } }); });

// Start server const server = app.listen(port, () => { console.log(Server running in ${process.env.NODE_ENV || 'development'} mode on port ${port}); });

// Graceful shutdown process.on('SIGTERM', () => { console.log('SIGTERM signal received: closing HTTP server'); server.close(() => { console.log('HTTP server closed'); }); });

module.exports = app;

Middleware Patterns

Middleware Execution Order

Order matters:

// 1. Security middleware (first) app.use(helmet());

// 2. Logging app.use(morgan('combined'));

// 3. Body parsing app.use(express.json()); app.use(express.urlencoded({ extended: true }));

// 4. CORS app.use(cors());

// 5. Static files app.use(express.static('public'));

// 6. Custom middleware app.use(customMiddleware);

// 7. Routes app.use('/api', apiRoutes);

// 8. 404 handler (after all routes) app.use((req, res) => { res.status(404).json({ error: 'Not Found' }); });

// 9. Error handler (last) app.use((err, req, res, next) => { // Error handling });

Built-in Middleware

express.json() - Parse JSON request bodies:

app.use(express.json({ limit: '10mb' })); // Set size limit

// Now req.body contains parsed JSON app.post('/api/data', (req, res) => { console.log(req.body); // { key: 'value' } res.json({ received: req.body }); });

express.urlencoded() - Parse URL-encoded bodies:

app.use(express.urlencoded({ extended: true }));

// Handles form submissions app.post('/form', (req, res) => { console.log(req.body); // { name: 'John', email: 'john@example.com' } });

express.static() - Serve static files:

// Serve files from 'public' directory app.use(express.static('public'));

// With options app.use(express.static('public', { maxAge: '1d', // Cache for 1 day etag: true, lastModified: true, index: 'index.html' }));

// Multiple static directories app.use(express.static('public')); app.use('/uploads', express.static('uploads'));

Custom Middleware

Simple middleware:

// Logging middleware const logger = (req, res, next) => { console.log(${req.method} ${req.path}); next(); // MUST call next() to continue };

app.use(logger);

Async middleware:

const asyncMiddleware = async (req, res, next) => { try { // Async operations const data = await fetchData(); req.data = data; next(); } catch (error) { next(error); // Pass errors to error handler } };

app.use(asyncMiddleware);

Conditional middleware:

const devOnly = (req, res, next) => { if (process.env.NODE_ENV === 'development') { return next(); } res.status(403).json({ error: 'Development only' }); };

app.get('/debug', devOnly, (req, res) => { res.json({ debug: 'info' }); });

Error handling middleware (must have 4 parameters):

app.use((err, req, res, next) => { console.error(err.stack);

// Handle specific error types if (err.name === 'ValidationError') { return res.status(400).json({ error: err.message }); }

if (err.name === 'UnauthorizedError') { return res.status(401).json({ error: 'Unauthorized' }); }

// Generic error res.status(err.status || 500).json({ error: process.env.NODE_ENV === 'production' ? 'Internal Server Error' : err.message }); });

Routing Best Practices

Route Organization

Inline routes (small apps):

app.get('/', (req, res) => res.send('Home')); app.get('/about', (req, res) => res.send('About')); app.post('/api/users', (req, res) => {/* ... */});

Router modules (recommended):

// routes/api.js const express = require('express'); const router = express.Router();

router.get('/users', (req, res) => { res.json({ users: [] }); });

router.post('/users', (req, res) => { res.json({ created: true }); });

module.exports = router;

// server.js const apiRoutes = require('./routes/api'); app.use('/api', apiRoutes);

Grouped routes by resource:

// routes/users.js const router = express.Router();

router.get('/', getAllUsers); router.get('/:id', getUser); router.post('/', createUser); router.put('/:id', updateUser); router.delete('/:id', deleteUser);

module.exports = router;

// server.js app.use('/api/users', require('./routes/users'));

Route Parameters

Path parameters:

app.get('/users/:id', (req, res) => { const userId = req.params.id; res.json({ userId }); });

// Multiple parameters app.get('/users/:userId/posts/:postId', (req, res) => { const { userId, postId } = req.params; res.json({ userId, postId }); });

Query parameters:

app.get('/search', (req, res) => { const { q, page, limit } = req.query; // /search?q=test&page=2&limit=10 res.json({ query: q, page, limit }); });

Route parameter validation:

// Middleware to validate ID const validateId = (req, res, next) => { const id = parseInt(req.params.id); if (isNaN(id) || id < 1) { return res.status(400).json({ error: 'Invalid ID' }); } req.params.id = id; // Convert to number next(); };

app.get('/users/:id', validateId, (req, res) => { // req.params.id is now a number });

HTTP Methods

RESTful API routes:

const router = express.Router();

// GET - Retrieve resources router.get('/items', (req, res) => { res.json({ items: [] }); });

router.get('/items/:id', (req, res) => { res.json({ item: {} }); });

// POST - Create resource router.post('/items', (req, res) => { const newItem = req.body; res.status(201).json({ created: newItem }); });

// PUT - Update entire resource router.put('/items/:id', (req, res) => { const updated = req.body; res.json({ updated }); });

// PATCH - Partial update router.patch('/items/:id', (req, res) => { const updates = req.body; res.json({ updated: updates }); });

// DELETE - Remove resource router.delete('/items/:id', (req, res) => { res.status(204).send(); // No content });

Async/Await Error Handling

The Problem

Without proper handling:

// BAD - Unhandled promise rejection app.get('/users', async (req, res) => { const users = await fetchUsers(); // If this throws, app crashes res.json(users); });

Solutions

Option 1: Try-catch in every route:

app.get('/users', async (req, res) => { try { const users = await fetchUsers(); res.json(users); } catch (error) { console.error(error); res.status(500).json({ error: error.message }); } });

Option 2: Async wrapper utility (recommended):

// utils/asyncHandler.js const asyncHandler = (fn) => (req, res, next) => { Promise.resolve(fn(req, res, next)).catch(next); };

module.exports = asyncHandler;

// Usage const asyncHandler = require('./utils/asyncHandler');

app.get('/users', asyncHandler(async (req, res) => { const users = await fetchUsers(); // Errors automatically caught res.json(users); }));

Option 3: express-async-errors package:

// Install: npm install express-async-errors require('express-async-errors'); // At the top of your app

// Now async errors are automatically caught app.get('/users', async (req, res) => { const users = await fetchUsers(); res.json(users); });

// Error handler catches async errors app.use((err, req, res, next) => { res.status(500).json({ error: err.message }); });

Custom Error Classes

// errors/AppError.js class AppError extends Error { constructor(message, statusCode) { super(message); this.statusCode = statusCode; this.status = ${statusCode}.startsWith('4') ? 'fail' : 'error'; this.isOperational = true;

Error.captureStackTrace(this, this.constructor);

} }

module.exports = AppError;

// Usage const AppError = require('./errors/AppError');

app.get('/users/:id', async (req, res, next) => { const user = await User.findById(req.params.id);

if (!user) { return next(new AppError('User not found', 404)); }

res.json(user); });

// Error handler app.use((err, req, res, next) => { err.statusCode = err.statusCode || 500; err.status = err.status || 'error';

res.status(err.statusCode).json({ status: err.status, message: err.message, ...(process.env.NODE_ENV === 'development' && { stack: err.stack }) }); });

Request/Response Patterns

Request Object

Common properties:

app.post('/api/data', (req, res) => { // URL parameters const { id } = req.params; // /api/data/:id

// Query string const { page, limit } = req.query; // /api/data?page=1&limit=10

// Request body (requires express.json()) const data = req.body;

// Headers const contentType = req.get('Content-Type'); const auth = req.headers.authorization;

// Request info const method = req.method; // POST const path = req.path; // /api/data const url = req.url; // /api/data?page=1 const protocol = req.protocol; // http or https const ip = req.ip; // Client IP

// Cookies (requires cookie-parser) const sessionId = req.cookies.sessionId; });

Response Methods

Send responses:

// Send JSON res.json({ message: 'Success' }); res.status(201).json({ created: true });

// Send text res.send('Plain text');

// Send HTML res.send('<h1>HTML</h1>');

// Send file res.sendFile('/path/to/file.pdf');

// Download file res.download('/path/to/file.pdf', 'filename.pdf');

// Redirect res.redirect('/new-url'); res.redirect(301, '/permanent-redirect');

// Set status res.status(404).json({ error: 'Not Found' }); res.sendStatus(204); // No Content

Set headers:

res.set('Content-Type', 'application/json'); res.set({ 'Content-Type': 'application/json', 'X-Custom-Header': 'value' });

// Set cookies res.cookie('sessionId', '12345', { maxAge: 900000, httpOnly: true, secure: true, sameSite: 'strict' });

// Clear cookies res.clearCookie('sessionId');

Response Patterns

Success responses:

// 200 OK - General success res.json({ data: items });

// 201 Created - Resource created res.status(201).json({ id: newId, created: true });

// 204 No Content - Success with no response body res.status(204).send();

Error responses:

// 400 Bad Request - Invalid input res.status(400).json({ error: 'Invalid email format' });

// 401 Unauthorized - Authentication required res.status(401).json({ error: 'Authentication required' });

// 403 Forbidden - Insufficient permissions res.status(403).json({ error: 'Access denied' });

// 404 Not Found - Resource doesn't exist res.status(404).json({ error: 'User not found' });

// 409 Conflict - Resource conflict res.status(409).json({ error: 'Email already exists' });

// 422 Unprocessable Entity - Validation error res.status(422).json({ error: 'Validation failed', details: validationErrors });

// 500 Internal Server Error - Server error res.status(500).json({ error: 'Internal server error' });

Proxy Patterns with Axios

Basic Proxy

const express = require('express'); const axios = require('axios');

const app = express(); app.use(express.json());

app.post('/api/proxy', async (req, res) => { try { const response = await axios.post( 'https://external-api.com/endpoint', req.body, { headers: { 'Content-Type': 'application/json' } } );

res.json(response.data);

} catch (error) { console.error('Proxy error:', error.message); res.status(error.response?.status || 500).json({ error: 'Proxy request failed', details: error.response?.data || error.message }); } });

Advanced Proxy with Headers

app.post('/api/proxy', async (req, res) => { try { // Forward headers from client const headers = { 'Content-Type': 'application/json', 'Authorization': req.headers.authorization, 'User-Agent': req.headers['user-agent'] };

const response = await axios.post(
  'https://external-api.com/endpoint',
  req.body,
  {
    headers,
    timeout: 5000, // 5 second timeout
    validateStatus: (status) => status &#x3C; 500 // Don't throw on 4xx
  }
);

// Forward response headers
res.set('X-Response-Time', response.headers['x-response-time']);

res.status(response.status).json(response.data);

} catch (error) { if (error.code === 'ECONNABORTED') { return res.status(504).json({ error: 'Gateway timeout' }); }

res.status(error.response?.status || 500).json({
  error: 'Proxy request failed',
  originalError: error.response?.data || null
});

} });

Proxy with Retry Logic

const axios = require('axios'); const axiosRetry = require('axios-retry');

// Configure axios with retry axiosRetry(axios, { retries: 3, retryDelay: axiosRetry.exponentialDelay, retryCondition: (error) => { return axiosRetry.isNetworkOrIdempotentRequestError(error) || error.response?.status === 429; // Retry on rate limit } });

app.post('/api/proxy', async (req, res) => { try { const response = await axios.post( 'https://external-api.com/endpoint', req.body, { headers: { 'Content-Type': 'application/json' }, 'axios-retry': { retries: 3 } } );

res.json(response.data);

} catch (error) { res.status(error.response?.status || 500).json({ error: 'Request failed after retries', details: error.response?.data }); } });

Request Validation Before Proxying

const Joi = require('joi');

const requestSchema = Joi.object({ keyword: Joi.string().max(100), startDate: Joi.date().iso(), endDate: Joi.date().iso().min(Joi.ref('startDate')) });

app.post('/api/search', async (req, res) => { // Validate request body const { error, value } = requestSchema.validate(req.body);

if (error) { return res.status(400).json({ error: 'Validation failed', details: error.details }); }

try { const response = await axios.post( 'https://external-api.com/search', value, // Use validated data { headers: { 'Content-Type': 'application/json' } } );

res.json(response.data);

} catch (error) { res.status(error.response?.status || 500).json({ error: 'Search request failed', details: error.response?.data }); } });

Security Best Practices

Helmet - Security Headers

const helmet = require('helmet');

// Basic usage app.use(helmet());

// Custom configuration app.use( helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], scriptSrc: ["'self'"], imgSrc: ["'self'", "data:", "https:"] } }, hsts: { maxAge: 31536000, includeSubDomains: true, preload: true } }) );

CORS Configuration

const cors = require('cors');

// Allow all origins (development only) app.use(cors());

// Production configuration app.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(',') || 'https://myapp.com', methods: ['GET', 'POST', 'PUT', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization'], credentials: true, maxAge: 86400 // 24 hours }));

// Dynamic origin validation app.use(cors({ origin: (origin, callback) => { const allowedOrigins = ['https://myapp.com', 'https://app.example.com'];

if (!origin || allowedOrigins.includes(origin)) {
  callback(null, true);
} else {
  callback(new Error('Not allowed by CORS'));
}

} }));

Rate Limiting

const rateLimit = require('express-rate-limit');

// Global rate limit const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // Limit each IP to 100 requests per windowMs message: 'Too many requests, please try again later.', standardHeaders: true, legacyHeaders: false });

app.use(limiter);

// Route-specific rate limit const strictLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 5, message: 'Too many login attempts, please try again later.' });

app.post('/api/login', strictLimiter, (req, res) => { // Login logic });

Input Validation and Sanitization

const { body, validationResult } = require('express-validator');

app.post('/api/users', // Validation middleware body('email').isEmail().normalizeEmail(), body('password').isLength({ min: 8 }).trim(), body('name').trim().escape(),

(req, res) => { const errors = validationResult(req);

if (!errors.isEmpty()) {
  return res.status(400).json({ errors: errors.array() });
}

// Proceed with validated data
const { email, password, name } = req.body;
res.json({ created: true });

} );

Prevent Parameter Pollution

const hpp = require('hpp');

// Prevent query parameter pollution app.use(hpp());

// Whitelist certain parameters that can be arrays app.use(hpp({ whitelist: ['tags', 'categories'] }));

Performance Optimization

Compression

const compression = require('compression');

app.use(compression({ filter: (req, res) => { if (req.headers['x-no-compression']) { return false; } return compression.filter(req, res); }, level: 6 // Compression level (0-9) }));

Caching Headers

// Static files with caching app.use(express.static('public', { maxAge: '1d', etag: true, lastModified: true }));

// API responses with cache control app.get('/api/data', (req, res) => { res.set('Cache-Control', 'public, max-age=300'); // 5 minutes res.json({ data: [] }); });

// No cache for dynamic data app.get('/api/user/profile', (req, res) => { res.set('Cache-Control', 'no-store, no-cache, must-revalidate, private'); res.json({ user: {} }); });

Response Time Tracking

const responseTime = require('response-time');

app.use(responseTime((req, res, time) => { console.log(${req.method} ${req.url} - ${time.toFixed(2)}ms); }));

// Or send as header app.use(responseTime());

Connection Pooling (for databases)

// Example with PostgreSQL const { Pool } = require('pg');

const pool = new Pool({ host: process.env.DB_HOST, port: process.env.DB_PORT, database: process.env.DB_NAME, user: process.env.DB_USER, password: process.env.DB_PASSWORD, max: 20, // Maximum pool size idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000 });

app.get('/api/users', async (req, res) => { const client = await pool.connect(); try { const result = await client.query('SELECT * FROM users'); res.json(result.rows); } finally { client.release(); } });

Production Configuration

Environment-based Configuration

const express = require('express'); const app = express();

const isDevelopment = process.env.NODE_ENV === 'development'; const isProduction = process.env.NODE_ENV === 'production';

// Development-only middleware if (isDevelopment) { const morgan = require('morgan'); app.use(morgan('dev')); }

// Production-only middleware if (isProduction) { app.use(require('compression')()); app.use(require('helmet')()); }

// Error handling app.use((err, req, res, next) => { console.error(err.stack);

res.status(err.status || 500).json({ error: isProduction ? 'Internal Server Error' : err.message, ...(isDevelopment && { stack: err.stack }) }); });

Graceful Shutdown

const express = require('express'); const app = express(); const port = process.env.PORT || 3000;

const server = app.listen(port, () => { console.log(Server running on port ${port}); });

// Handle shutdown signals const gracefulShutdown = (signal) => { console.log(\n${signal} signal received: closing HTTP server);

server.close(() => { console.log('HTTP server closed');

// Close database connections, cleanup resources
// db.close();

process.exit(0);

});

// Force close after 10 seconds setTimeout(() => { console.error('Forcing shutdown'); process.exit(1); }, 10000); };

process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); process.on('SIGINT', () => gracefulShutdown('SIGINT'));

// Handle uncaught exceptions process.on('uncaughtException', (error) => { console.error('Uncaught Exception:', error); gracefulShutdown('uncaughtException'); });

process.on('unhandledRejection', (reason, promise) => { console.error('Unhandled Rejection at:', promise, 'reason:', reason); gracefulShutdown('unhandledRejection'); });

Logging

const winston = require('winston'); const morgan = require('morgan');

// Winston logger const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ new winston.transports.File({ filename: 'error.log', level: 'error' }), new winston.transports.File({ filename: 'combined.log' }) ] });

if (process.env.NODE_ENV !== 'production') { logger.add(new winston.transports.Console({ format: winston.format.simple() })); }

// Morgan for HTTP logging app.use(morgan('combined', { stream: { write: (message) => logger.info(message.trim()) } }));

// Use logger in routes app.get('/api/data', async (req, res) => { try { const data = await fetchData(); logger.info('Data fetched successfully'); res.json(data); } catch (error) { logger.error('Error fetching data:', { error: error.message, stack: error.stack }); res.status(500).json({ error: 'Internal server error' }); } });

Testing Express Applications

Setup with Jest and Supertest

// Install: npm install --save-dev jest supertest

// server.test.js const request = require('supertest'); const app = require('./server');

describe('GET /', () => { it('responds with 200 status', async () => { const response = await request(app).get('/'); expect(response.status).toBe(200); });

it('responds with JSON', async () => { const response = await request(app).get('/api/users'); expect(response.headers['content-type']).toMatch(/json/); }); });

describe('POST /api/users', () => { it('creates a user with valid data', async () => { const userData = { name: 'John', email: 'john@example.com' };

const response = await request(app)
  .post('/api/users')
  .send(userData)
  .set('Content-Type', 'application/json');

expect(response.status).toBe(201);
expect(response.body).toHaveProperty('id');

});

it('rejects invalid email', async () => { const response = await request(app) .post('/api/users') .send({ name: 'John', email: 'invalid' });

expect(response.status).toBe(400);

}); });

Mocking External APIs

const axios = require('axios'); jest.mock('axios');

describe('POST /api/proxy', () => { it('proxies request successfully', async () => { const mockData = { result: 'success' }; axios.post.mockResolvedValue({ data: mockData });

const response = await request(app)
  .post('/api/proxy')
  .send({ query: 'test' });

expect(response.status).toBe(200);
expect(response.body).toEqual(mockData);
expect(axios.post).toHaveBeenCalledWith(
  expect.any(String),
  { query: 'test' },
  expect.any(Object)
);

});

it('handles proxy errors', async () => { axios.post.mockRejectedValue(new Error('Network error'));

const response = await request(app)
  .post('/api/proxy')
  .send({ query: 'test' });

expect(response.status).toBe(500);
expect(response.body).toHaveProperty('error');

}); });

Common Express Issues

Headers Already Sent

Problem:

// BAD - sends headers twice app.get('/data', (req, res) => { res.json({ data: [] }); res.status(200).send(); // Error: headers already sent });

Solution:

// GOOD - single response app.get('/data', (req, res) => { return res.json({ data: [] }); // Use return to prevent further execution });

// Or use else app.get('/data', (req, res) => { if (error) { return res.status(500).json({ error: 'Failed' }); } else { return res.json({ data: [] }); } });

Middleware Not Running

Problem: Middleware defined after routes

// BAD - middleware defined after route app.get('/api/data', (req, res) => { res.json({ data: req.user }); // req.user is undefined });

app.use(authMiddleware); // Too late!

Solution: Define middleware before routes

// GOOD - middleware before routes app.use(authMiddleware);

app.get('/api/data', (req, res) => { res.json({ data: req.user }); // req.user exists });

Forgot to call next()

Problem:

// BAD - middleware doesn't call next() app.use((req, res) => { console.log('Request received'); // Request hangs here! });

Solution:

// GOOD - always call next() app.use((req, res, next) => { console.log('Request received'); next(); // Continue to next middleware });

CORS Errors

Problem: Missing CORS headers

// Frontend gets CORS error

Solution:

const cors = require('cors'); app.use(cors());

// Or manual headers app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE'); res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');

if (req.method === 'OPTIONS') { return res.sendStatus(200); }

next(); });

Body Parser Not Working

Problem: req.body is undefined

app.post('/api/data', (req, res) => { console.log(req.body); // undefined });

Solution: Add body parser middleware

app.use(express.json()); // Parse JSON app.use(express.urlencoded({ extended: true })); // Parse URL-encoded

app.post('/api/data', (req, res) => { console.log(req.body); // Now it works });

Best Practices Summary

Application Structure

  • Use environment variables for configuration

  • Implement graceful shutdown

  • Use middleware in correct order

  • Separate routes into modules

  • Implement proper error handling

Security

  • Always use helmet for security headers

  • Configure CORS appropriately

  • Implement rate limiting

  • Validate and sanitize all inputs

  • Never expose sensitive errors in production

  • Use HTTPS in production

  • Keep dependencies updated

Performance

  • Use compression middleware

  • Implement caching where appropriate

  • Use connection pooling for databases

  • Minimize middleware stack

  • Use async/await instead of callbacks

Error Handling

  • Use async error wrapper or express-async-errors

  • Create custom error classes

  • Centralize error handling middleware

  • Log errors appropriately

  • Never crash on unhandled errors

Code Quality

  • Use consistent naming conventions

  • Keep route handlers small and focused

  • Extract business logic into separate modules

  • Write tests for routes and middleware

  • Use TypeScript for larger applications

Production Readiness

  • Set NODE_ENV=production

  • Implement logging (Winston, Morgan)

  • Use process managers (PM2, Docker)

  • Monitor application health

  • Implement graceful shutdown

  • Handle uncaught exceptions

Resources

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

google-cloud-build-expert

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

usaspending-api-helper

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

testing-best-practices

No summary provided by upstream source.

Repository SourceNeeds Review