@flavoai/fastfold
Version:
Zero-boilerplate backend for React apps with auto-generated CRUD and declarative security
307 lines • 11.5 kB
JavaScript
export class ApiDocumentationGenerator {
config;
constructor(config) {
this.config = config;
}
/**
* Generate complete API documentation
*/
generateDocumentation() {
const tables = this.config.tables;
const endpoints = [];
// Add health endpoint
endpoints.push({
method: 'GET',
path: '/health',
description: 'Health check endpoint',
responses: [
{
status: 200,
description: 'Server is healthy',
schema: {
type: 'object',
properties: {
status: { type: 'string', example: 'healthy' },
timestamp: { type: 'string', example: '2024-01-01T00:00:00.000Z' }
}
}
}
]
});
// Generate endpoints for each table
for (const [tableName, tableDefinition] of Object.entries(tables)) {
const tableEndpoints = this.generateTableEndpoints(tableName, tableDefinition);
endpoints.push(...tableEndpoints);
}
return {
info: {
title: 'Fastfold API',
description: 'Auto-generated CRUD API with declarative security',
version: '1.0.0',
baseUrl: 'http://localhost:' + (this.config.port || 3001)
},
tables: Object.fromEntries(Object.entries(tables).map(([name, def]) => [
name,
{
schema: def.schema,
security: this.getSecurityDescription(def.security)
}
])),
endpoints
};
}
/**
* Generate endpoints for a specific table
*/
generateTableEndpoints(tableName, tableDefinition) {
const basePath = `/api/${tableName}`;
const schema = tableDefinition.schema;
const endpoints = [];
// GET /api/:table - List records
endpoints.push({
method: 'GET',
path: basePath,
description: `List all ${tableName} records with optional filtering, sorting, and pagination`,
parameters: [
{
name: 'params',
in: 'query',
required: false,
type: 'string',
description: 'JSON string with query parameters (where, orderBy, take, skip)'
}
],
responses: [
{
status: 200,
description: 'List of records',
schema: {
type: 'object',
properties: {
success: { type: 'boolean', example: true },
data: {
type: 'array',
items: this.schemaToJsonSchema(schema)
},
count: { type: 'number', example: 10 }
}
}
},
{ status: 403, description: 'Forbidden - Security check failed' }
]
});
// GET /api/:table/:id - Get single record
endpoints.push({
method: 'GET',
path: `${basePath}/{id}`,
description: `Get a specific ${tableName} record by ID`,
parameters: [
{
name: 'id',
in: 'path',
required: true,
type: 'string',
description: 'Record ID'
}
],
responses: [
{
status: 200,
description: 'Single record',
schema: {
type: 'object',
properties: {
success: { type: 'boolean', example: true },
data: this.schemaToJsonSchema(schema)
}
}
},
{ status: 404, description: 'Record not found' },
{ status: 403, description: 'Forbidden - Security check failed' }
]
});
// POST /api/:table - Create record
endpoints.push({
method: 'POST',
path: basePath,
description: `Create a new ${tableName} record`,
requestBody: {
description: `${tableName} data to create`,
schema: this.schemaToJsonSchema(schema)
},
responses: [
{
status: 201,
description: 'Record created successfully',
schema: {
type: 'object',
properties: {
success: { type: 'boolean', example: true },
data: this.schemaToJsonSchema(schema)
}
}
},
{ status: 400, description: 'Validation error' },
{ status: 403, description: 'Forbidden - Security check failed' }
]
});
// PUT /api/:table/:id - Update record
endpoints.push({
method: 'PUT',
path: `${basePath}/{id}`,
description: `Update a ${tableName} record`,
parameters: [
{
name: 'id',
in: 'path',
required: true,
type: 'string',
description: 'Record ID'
}
],
requestBody: {
description: `${tableName} data to update`,
schema: this.schemaToJsonSchema(schema)
},
responses: [
{
status: 200,
description: 'Record updated successfully',
schema: {
type: 'object',
properties: {
success: { type: 'boolean', example: true },
data: this.schemaToJsonSchema(schema)
}
}
},
{ status: 404, description: 'Record not found' },
{ status: 400, description: 'Validation error' },
{ status: 403, description: 'Forbidden - Security check failed' }
]
});
// DELETE /api/:table/:id - Delete record
endpoints.push({
method: 'DELETE',
path: `${basePath}/{id}`,
description: `Delete a ${tableName} record`,
parameters: [
{
name: 'id',
in: 'path',
required: true,
type: 'string',
description: 'Record ID'
}
],
responses: [
{
status: 200,
description: 'Record deleted successfully',
schema: {
type: 'object',
properties: {
success: { type: 'boolean', example: true },
data: {
type: 'object',
properties: {
deleted: { type: 'boolean', example: true }
}
}
}
}
},
{ status: 404, description: 'Record not found' },
{ status: 403, description: 'Forbidden - Security check failed' }
]
});
// GET /api/:table/count - Count records
endpoints.push({
method: 'GET',
path: `${basePath}/count`,
description: `Count ${tableName} records with optional filtering`,
parameters: [
{
name: 'where',
in: 'query',
required: false,
type: 'string',
description: 'JSON string with filter conditions'
}
],
responses: [
{
status: 200,
description: 'Record count',
schema: {
type: 'object',
properties: {
success: { type: 'boolean', example: true },
data: {
type: 'object',
properties: {
count: { type: 'number', example: 42 }
}
}
}
}
},
{ status: 403, description: 'Forbidden - Security check failed' }
]
});
return endpoints;
}
/**
* Convert Fastfold schema to JSON Schema
*/
schemaToJsonSchema(schema) {
const properties = {};
for (const [fieldName, fieldType] of Object.entries(schema)) {
switch (fieldType) {
case 'string':
properties[fieldName] = { type: 'string', example: `sample_${fieldName}` };
break;
case 'number':
properties[fieldName] = { type: 'number', example: 42 };
break;
case 'boolean':
properties[fieldName] = { type: 'boolean', example: true };
break;
case 'date':
properties[fieldName] = { type: 'string', format: 'date-time', example: '2024-01-01T00:00:00.000Z' };
break;
case 'json':
properties[fieldName] = { type: 'object', example: { key: 'value' } };
break;
default:
properties[fieldName] = { type: 'string', example: `sample_${fieldName}` };
}
}
return {
type: 'object',
properties,
example: Object.fromEntries(Object.entries(properties).map(([key, value]) => [key, value.example]))
};
}
/**
* Get human-readable security description
*/
getSecurityDescription(security) {
if (!security)
return 'No security configured';
// This is a simplified version - in a real implementation,
// you'd need to inspect the security object more thoroughly
if (security.type === 'public')
return 'Public access - no authentication required';
if (security.type === 'admin')
return 'Admin only - requires admin role';
if (security.type === 'owner')
return `Owner-based access - users can only access their own records`;
if (security.type === 'team')
return `Team-based access - users can access records from their team`;
if (security.type === 'authenticated')
return 'Authenticated users only';
return 'Custom security rule';
}
}
//# sourceMappingURL=generator.js.map