api-development-standards
Node.js/Express/Prisma/PostgreSQL development patterns for Design Token Manager API. Use when working with controllers, routes, middleware, DAL, or database operations. Enforces multi-tenant isolation, Prisma best practices, Express patterns, authentication, and API response standards.
DeepseekModel
官方收录技能
质量 良好 · 48
v1.0.0
获取
https://deepseekmodel.com/api/download.php?id=mbelenmontoya-design-token-manager-api-claude-skills-api-development-standards-skill-md&format=skill
下载 .skill
标准格式,含 system_prompt 与 model_config,导入任意 Agent 框架即可使用
.skill 文件中 system_prompt 字段的实际内容。
name api-development-standards description Node.js/Express/Prisma/PostgreSQL development patterns for Design Token Manager API. Use when working with controllers, routes, middleware, DAL, or database operations. Enforces multi-tenant isolation, Prisma best practices, Express patterns, authentication, and API response standards. 🔧 API Development Standards Enforces architectural patterns and best practices for the Design Token Manager API (Node.js/Express/Prisma/PostgreSQL). When to Use This Skill ALWAYS activate when: Creating or modifying API routes Building controllers or services Database operations with Prisma Adding middleware (auth, validation, tenant context) Multi-tenant data operations API authentication (JWT, API keys) Error handling and validation Writing tests for endpoints Architecture Overview Three-Layer Architecture ROUTES (Express) ↓ CONTROLLERS (Business Logic) ↓ DAL (Database Abstraction Layer) ↓ PRISMA (ORM) ↓ POSTGRESQL (Supabase) Key principles: Routes handle HTTP concerns only (validation, auth) Controllers contain business logic DAL provides database abstraction Prisma performs type-safe queries All data operations enforce tenant/project isolation Core Standards 1. Multi-Tenant Data Isolation CRITICAL : Every database query MUST include tenant/project filtering. ✅ CORRECT: // Controller with tenant/project filtering async function getTokens ( req, res, next ) { const { tenantId, projectId } = req. tenant ; // From tenantContext middleware const tokens = await databaseService. findTokensByProject (tenantId, projectId); res. json ({ tokens }); } ❌ WRONG: // Missing tenant/project isolation - SECURITY VULNERABILITY async function getTokens ( req, res, next ) { const tokens = await databaseService. findAllTokens (); // NO FILTERING! res. json ({ tokens }); } Rules: NEVER query without tenant/project filtering Use tenantContext middleware to extract tenant/project IDs DAL methods already enforce isolation - use them Test multi-tenant isolation in integration tests 2. Database Access via DAL ALWAYS use the Database Abstraction Layer, never direct Prisma calls in controllers. ✅ CORRECT: import databaseService from '../dal/DatabaseService.js' ; async function createToken ( req, res, next ) { const { tenantId, projectId } = req. tenant ; const tokenData = req. body ; const token = await databaseService. createToken ({ ...tokenData, tenantId, projectId }); res. status ( 201 ). json ({ token }); } ❌ WRONG: import prisma from '../config/prisma.js' ; async function createToken ( req, res, next ) { // Direct Prisma access bypasses DAL abstraction const token = await prisma. token . create ({ data : req. body }); res. json ({ token }); } Why: DAL provides database flexibility (could switch from Supabase) DAL methods enforce tenant isolation Consistent patterns across controllers Easier testing with DAL mocks 3. Controller Structure Controllers should follow this pattern: import databaseService from '../dal/DatabaseService.js' ; /** * Get all tokens for a project * @route GET /api/v1/tokens */ export async function listTokens ( req, res, next ) { try { const { tenantId, projectId } = req. tenant ; // From middleware const { page = 1 , limit = 20 , category } = req. query ; // Business logic const tokens = await databaseService. findTokensByProject ( tenantId, projectId, { page, limit, category } ); // Response res. json ({ tokens, pagination : { page, limit, total : tokens. length } }); } catch (error) { next (error); // Pass to error handler } } /** * Create a new token * @route POST /api/v1/tokens */ export async function createToken ( req, res, next ) { try { const { tenantId, projectId } = req. tenant ; const tokenData = req. body ; // Validation happens in route middleware // Business logic here const token = await databaseService. createToken ({ ...tokenData, tenantId, projectId }); res. status ( 201 ). json ({ token }); } catch (error) { next (error); } } Key points: Try/catch with next(error) for centralized error handling Extract tenant/project from req.tenant (middleware) JSDoc comments for route documentation Return consistent JSON responses Proper HTTP status codes (201 for created, 200 for success) 4. Route Structure Routes handle HTTP concerns, delegate to controllers: import express from 'express' ; import { body, query } from 'express-validator' ; import { verifyJWT } from '../middleware/verifyJWT.js' ; import { tenantContext } from '../middleware/tenantContext.js' ; import { requireRole } from '../middleware/requireRole.js' ; import * as tokenController from '../controllers/tokenController.js' ; const router = express. Router (); /** * @swagger * /api/v1/tokens: * get: * summary: List tokens for a project * tags: [Tokens] * security: * - BearerAuth: [] */ router. get ( '/' , verifyJWT, // Authentication tenantContext, // Multi-tenant context requireRole ([ 'admin' , 'editor' , 'viewer' ]), // Authorization query ( 'category' ). optional (). isString (), // Validation tokenController. listTokens // Controller ); /** * @swagger * /api/v1/tokens: * post: * summary: Create a new token */ router. post ( '/' , verifyJWT, tenantContext, requireRole ([ 'admin' , 'editor' ]), // Only admin/editor can create body ( 'name' ). notEmpty (). isString (), body ( 'value' ). notEmpty (). isString (), body ( 'category' ). optional (). isString (), tokenController. createToken ); export default router; Middleware order matters: Authentication ( verifyJWT or verifyApiKey ) Tenant context ( tenantContext ) Authorization ( requireRole ) Validation ( express-validator ) Controller 5. Prisma/DAL Best Practices When working in the DAL layer: ✅ Use transactions for multiple operations: async createTokenWithCategory ( tokenData ) { return await this . prisma .$transaction( async (tx) => { const category = await tx. category . findUnique ({ where : { name : tokenData. category } }); if (!category) { throw new Error ( 'Category not found' ); } return await tx. token . create ({ data : tokenData }); }); } ✅ Use proper error handling: async findTokenById ( id ) { try { const token = await this . prisma . token . findUnique ({ where : { id }, include : { tenant : true , project : true } }); if (!token) { return null ; // Let controller handle 404 } return token; } catch (error) { throw new Error ( `Failed to find token: ${error.message} ` ); } } ✅ Use select/include for performance: // Only fetch needed fields const tokens = await this . prisma . token . findMany ({ where : { tenantId, projectId }, select : { id : true , name : true , value : true , category : true // Don't fetch tenantId, projectId, createdAt if not needed } }); 6. Authentication Patterns JWT Authentication (Admin Users): import jwt from 'jsonwebtoken' ; export function verifyJWT ( req, res, next ) { const token = req. headers . authorization ?. split ( ' ' )[ 1 ]; if (!token) { return res. status ( 401 ). json ({ message : 'Authentication required' }); } try { const decoded = jwt. verify (token, process. env . JWT_SECRET ); req. user = decoded; // Attach user to request next (); } catch (error) { return res. status ( 401 ). json ({ message : 'Invalid token' }); } } API Key Authentication (Programmatic Access): export async function verifyApiKey ( req, res, next ) { const apiKey = req. headers . authorization ?. replace ( 'Bearer ' , '' ); if (!apiKey) { return res. status ( 401 ). json ({ message : 'API key required' }); } const hashedKey = hashApiKey (apiKey); const keyRecord = await databaseService. findApiKeyByHash (hashedKey); if (!keyRecord || keyRecord. revoked ) { return res. status ( 401 ). json ({ message : 'Invalid API key' }); } req. tenant = { tenantId : keyRecord. tenantId , projectId : keyRecord. projectId }; req. apiKey = keyRecord; next (); } 7. Error Handling Centralized error handler: export function errorHandler ( err, req, res, next ) { console . error (err. stack ); // Prisma errors if (err. code === 'P2002' ) { return res. status ( 409 ). json ({ message : 'Resource already exists' , field : err. meta ?. target }); } // Validation errors if (err. name === 'ValidationError' ) { return res. status ( 400 ). json ({ message : 'Validation failed' , errors : err. errors }); } // Default error res. status (err. status || 500 ). json ({ message : err. message || 'Internal server error' }); } Custom error classes: export class NotFoundError extends Error { constructor ( resource ) { super ( ` ${resource} not found` ); this . name = 'NotFoundError' ; this . status = 404 ; } } export class ForbiddenError extends Error { constructor ( message = 'Access forbidden' ) { super (message); this . name = 'ForbiddenError' ; this . status = 403 ; } } 8. Response Standards Consistent response format: ✅ Success (200/201): res. json ({ token : { id, name, value, category } }); // Or with pagination res. json ({ tokens : [...], pagination : { page : 1 , limit : 20 , total : 45 , pages : 3 } }); ✅ Created (201): res. status ( 201 ). json ({ token : newToken, message : 'Token created successfully' }); ✅ Error (4xx/5xx): res. status ( 404 ). json ({ message : 'Token not found' , code : 'RESOURCE_NOT_FOUND' }); 9. Testing Standards Integration tests for endpoints: import request from 'supertest' ; import app from '../src/index.js' ; import databaseService from '../src/dal/DatabaseService.js' ; describe ( 'POST /api/v1/tokens' , () => { let authToken; let tenantId; let projectId; beforeAll ( async () => { // Setup test data const tenant = await databaseService. createTenant ({ name : 'Test' }); tenantId = tenant. id ; const project = await databaseService. createProject ({ name : 'Test Project' , tenantId }); projectId = project. id ; // Get JWT token authToken = 'test-jwt-token' ; }); it ( 'should create a token with valid data' , async () => { const response = await request (app) . post ( '/api/v1/tokens' ) . set ( 'Authorization' , `Bearer ${authToken} ` ) . set ( 'X-Tenant-ID' , tenantId) . set ( 'X-Project-ID' , projectId) . send ({ name : 'primary-color' , value : '#007bff' , category : 'color' }); expect (response. status ). toBe ( 201 ); expect (response. body . token ). toHaveProperty ( 'id' ); expect (response. body . token . name ). toBe ( 'primary-color' ); });
Agent 识别该技能的关键词,点击任意一个即可复制。
该技能未提供触发词。
下载的 .skill 包内含以下字段。
| 字段 | 说明 |
|---|---|
| format | 格式标识(skill/v1) |
| skill_id | 技能唯一 ID |
| name | 技能名称 |
| version | 版本号 |
| description | 技能描述 |
| category | 所属分类(数组) |
| trigger_words | 触发词列表 |
| tags | 标签列表 |
| source | 来源标识 |
| source_url | 来源链接(本页地址) |
| exported_at | 导出时间(每次下载生成) |
| system_prompt | 系统提示词正文 |
| model_config | 模型参数:provider / model / temperature / max_tokens / top_p |
| examples | 示例 |
| install_guide | 各平台导入说明(Coze / Dify / Claude / 自定义框架) |