{"id":34847999,"url":"https://github.com/enfyra/server","last_synced_at":"2026-04-25T20:02:30.029Z","repository":{"id":314063964,"uuid":"1051915688","full_name":"enfyra/server","owner":"enfyra","description":"Enfyra – an open-source, self-hosted BaaS with built-in clustering, letting you build fast, scale easily, and customize without limits.","archived":false,"fork":false,"pushed_at":"2026-04-21T11:26:54.000Z","size":5275,"stargazers_count":32,"open_issues_count":0,"forks_count":10,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-21T13:38:41.995Z","etag":null,"topics":["alternative","api","api-rest","backend","clustering","custom-logic","customizable","database","enfyra","generate-api","graphql","mariadb","mongodb","mysql","nestjs","postgresql","scaleable","schema"],"latest_commit_sha":null,"homepage":"https://demo.enfyra.io/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/enfyra.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-07T01:57:09.000Z","updated_at":"2026-04-21T11:26:32.000Z","dependencies_parsed_at":null,"dependency_job_id":"1216262b-7883-4d33-b5eb-3d6845979b40","html_url":"https://github.com/enfyra/server","commit_stats":null,"previous_names":["dothinh115/enfyra-be","enfyra/backend","enfyra/server"],"tags_count":83,"template":false,"template_full_name":null,"purl":"pkg:github/enfyra/server","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enfyra%2Fserver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enfyra%2Fserver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enfyra%2Fserver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enfyra%2Fserver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/enfyra","download_url":"https://codeload.github.com/enfyra/server/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/enfyra%2Fserver/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32274987,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-25T18:29:39.964Z","status":"ssl_error","status_checked_at":"2026-04-25T18:29:32.149Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alternative","api","api-rest","backend","clustering","custom-logic","customizable","database","enfyra","generate-api","graphql","mariadb","mongodb","mysql","nestjs","postgresql","scaleable","schema"],"created_at":"2025-12-25T18:55:28.322Z","updated_at":"2026-04-25T20:02:30.022Z","avatar_url":"https://github.com/enfyra.png","language":"TypeScript","readme":"# Enfyra Backend\n\n[Enfyra](https://demo.enfyra.io/login) is the open-source backend platform.  \nWe’re building the flexibility backend framework that automatically generates APIs from your database. You create tables through a visual interface, and Enfyra instantly provides REST \u0026 GraphQL APIs for them - no coding required. It's like having a backend developer that never sleeps.\n\n## Documentation\n\nFor full documentation, visit [docs](https://github.com/enfyra/documents)\n\nTo see how to contribute, visit [Contributing](https://github.com/enfyra/server/blob/main/CONTRIBUTING.md)\n\n## Community \u0026 Support\n\n- [Community Forum](https://github.com/orgs/enfyra/discussions)\n- [GitHub Issues](https://github.com/enfyra/server/issues)\n- [Discord](https://discord.gg/DH5sXtFVWM)\n\n## How it works\n**Architecture**\n\nEnfyra is a self-hosted and locally developed, easy-to-install. Cloud coming soon.\n\n- **Query Engine**: high-performance engine for filtering, joins, aggregates, and search directly through your API.\n- **Realtime**: push updates to clients when rows change using websockets.\n- **REST/GraphQL API**: automatically generated from your schema.\n- **Auth Service**: JWT-based authentication API for sign-ups, logins, and session management.\n- **Storage**: RESTful API for managing files and permissions.\n- **Functions**: run server-side code close to your data.\n\n## Database Migrations\n\nEnfyra supports schema and data migrations through JSON configuration files.\n\n### Schema Migration (`data/snapshot.json`)\n\nDefine your database schema (tables, columns, relations):\n\n```json\n{\n  \"my_table\": {\n    \"name\": \"my_table\",\n    \"description\": \"My custom table\",\n    \"columns\": [\n      { \"name\": \"id\", \"type\": \"int\", \"isPrimary\": true, \"isGenerated\": true },\n      { \"name\": \"name\", \"type\": \"varchar\", \"isNullable\": false }\n    ],\n    \"relations\": []\n  }\n}\n```\n\nRun schema migration:\n```bash\nnpx ts-node scripts/init-db.ts\n```\n\n### Schema Migration (`data/snapshot-migration.json`)\n\nFor dangerous operations (remove, modify/rename). Adding is handled automatically by `snapshot.json`.\n\n```json\n{\n  \"tables\": [\n    {\n      \"_unique\": { \"name\": { \"_eq\": \"users\" } },\n      \"columnsToModify\": [\n        { \"from\": { \"name\": \"email\" }, \"to\": { \"name\": \"userEmail\" } }\n      ],\n      \"columnsToRemove\": [\"deprecated_field\"],\n      \"relationsToModify\": [\n        { \"from\": { \"propertyName\": \"oldRelation\" }, \"to\": { \"propertyName\": \"newRelation\" } }\n      ],\n      \"relationsToRemove\": [\"deprecated_relation\"]\n    }\n  ],\n  \"tablesToDrop\": [\"old_table_name\"]\n}\n```\n\n**Operations:**\n\n| Field | Description | Data Loss Risk |\n|-------|-------------|----------------|\n| `columnsToModify` | Rename or change column properties | Low (rename preserves data) |\n| `columnsToRemove` | Remove columns | **HIGH** |\n| `relationsToModify` | Rename or change relation properties | Low |\n| `relationsToRemove` | Remove relations (drops FK column) | **HIGH** |\n| `tablesToDrop` | Drop entire tables | **HIGH** |\n\n**Flow:**\n1. Physical DB changes (init-db script or app bootstrap)\n2. Metadata updates (provision service)\n3. Both read from same `snapshot-migration.json` → consistent\n\n#### Usage Examples\n\n**1. Rename a column (preserves data)**\n\n```json\n{\n  \"tables\": [{\n    \"_unique\": { \"name\": { \"_eq\": \"users\" } },\n    \"columnsToModify\": [\n      { \"from\": { \"name\": \"email\" }, \"to\": { \"name\": \"userEmail\" } }\n    ]\n  }]\n}\n```\n\nResult: Column `email` renamed to `userEmail`, data preserved.\n\n**2. Change column properties**\n\n```json\n{\n  \"tables\": [{\n    \"_unique\": { \"name\": { \"_eq\": \"users\" } },\n    \"columnsToModify\": [\n      {\n        \"from\": { \"name\": \"status\", \"isNullable\": true },\n        \"to\": { \"name\": \"status\", \"isNullable\": false }\n      }\n    ]\n  }]\n}\n```\n\nResult: Column `status` becomes NOT NULL.\n\n**3. Remove deprecated column (⚠️ data loss)**\n\n```json\n{\n  \"tables\": [{\n    \"_unique\": { \"name\": { \"_eq\": \"users\" } },\n    \"columnsToRemove\": [\"old_legacy_field\"]\n  }]\n}\n```\n\nResult: Column `old_legacy_field` dropped, all data in this column lost.\n\n**4. Rename a relation (preserves FK data)**\n\n```json\n{\n  \"tables\": [{\n    \"_unique\": { \"name\": { \"_eq\": \"orders\" } },\n    \"relationsToModify\": [\n      { \"from\": { \"propertyName\": \"approvedBy\" }, \"to\": { \"propertyName\": \"approver\" } }\n    ]\n  }]\n}\n```\n\nResult: FK column `approvedById` renamed to `approverId`, data preserved.\n\n**5. Remove a relation (⚠️ FK data loss)**\n\n```json\n{\n  \"tables\": [{\n    \"_unique\": { \"name\": { \"_eq\": \"orders\" } },\n    \"relationsToRemove\": [\"legacyRelation\"]\n  }]\n}\n```\n\nResult: FK column dropped, all FK references lost.\n\n**6. Drop entire table (⚠️ all data lost)**\n\n```json\n{\n  \"tablesToDrop\": [\"deprecated_table\", \"legacy_data\"]\n}\n```\n\nResult: Tables completely removed from database.\n\n#### When to Use\n\n| Scenario | File to Modify |\n|----------|---------------|\n| Add new table | `snapshot.json` |\n| Add new column | `snapshot.json` |\n| Add new relation | `snapshot.json` |\n| Rename column | `snapshot-migration.json` |\n| Remove column | `snapshot-migration.json` |\n| Rename relation | `snapshot-migration.json` |\n| Remove relation | `snapshot-migration.json` |\n| Drop table | `snapshot-migration.json` |\n\n#### How It Works\n\n```\n┌─────────────────────────────────────┐\n│     snapshot-migration.json         │\n│         (single source)             │\n└──────────────┬──────────────────────┘\n               │\n       ┌───────┴───────┐\n       ▼               ▼\n┌──────────────┐ ┌──────────────────┐\n│  init-db.ts  │ │  provision service│\n│              │ │  (app bootstrap)  │\n│ Physical DB  │ │   Metadata DB     │\n│  (tables,    │ │  (table_def,      │\n│   columns,   │ │   column_def,     │\n│   FKs)       │ │   relation_def)   │\n└──────────────┘ └──────────────────┘\n       │               │\n       └───────┬───────┘\n               ▼\n         ✅ Consistent\n```\n\nBoth physical DB and metadata are updated from the same source, ensuring consistency.\n\n### Destructive schema changes via API (confirm-hash)\n\nWhen changing schema through the API (e.g. updating or deleting a row in `table_definition`), destructive changes are protected by a confirm-hash challenge.\n\n- The server returns **422** with `code = \"SCHEMA_CONFIRM_REQUIRED\"` and `details` including:\n  - `requiredConfirmHash`\n  - `confirmToken` (short-lived)\n  - `confirmTtlMs`\n  - `removedColumns`, `removedRelationsCount` (when applicable)\n- To proceed, resend the same request with:\n  - `x-schema-confirm-hash: \u003crequiredConfirmHash\u003e`\n  - `x-schema-confirm-token: \u003cconfirmToken\u003e`\n\nExample flow:\n\n```bash\n# 1) Attempt destructive update\ncurl -X PATCH \"http://localhost:1105/api/table_definition/\u003cid\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"columns\":[{\"name\":\"id\",\"type\":\"int\"}]}' \n\n# 2) Server responds 422 with details.requiredConfirmHash + details.confirmToken\n# 3) Retry with headers\ncurl -X PATCH \"http://localhost:1105/api/table_definition/\u003cid\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"x-schema-confirm-hash: \u003crequiredConfirmHash\u003e\" \\\n  -H \"x-schema-confirm-token: \u003cconfirmToken\u003e\" \\\n  -d '{\"columns\":[{\"name\":\"id\",\"type\":\"int\"}]}'\n```\n\nFor a limited transition period, the legacy `schemaConfirm` phrase may also be accepted, but the UI uses confirm-hash by default.\n\n### Data Migration (`data/data-migration.json`)\n\nMigrate existing data when the system is already initialized:\n\n```json\n{\n  \"_deletedTables\": [\"deprecated_table\"],\n  \"role_definition\": [\n    {\n      \"name\": \"Admin\",\n      \"description\": \"Updated admin description\",\n      \"_unique\": { \"name\": { \"_eq\": \"Admin\" } }\n    }\n  ]\n}\n```\n\n- `_deletedRecords`: Delete specific records by filter (safe way to remove seeded routes/menus/etc. across versions)\n- `_deletedTables`: Array of table names to delete all data from\n- Table entries: Data to migrate, using `_unique` to identify existing records\n\n#### Delete specific records (`_deletedRecords`)\n\nUse `_deletedRecords` when you need to remove a small set of rows (e.g. remove an old seeded `route_definition`) without wiping the entire table.\n\n```json\n{\n  \"_deletedRecords\": [\n    { \"table\": \"route_definition\", \"filter\": { \"path\": { \"_eq\": \"/old-route\" } } },\n    { \"table\": \"menu_definition\", \"filter\": { \"path\": { \"_eq\": \"/old-menu\" } } }\n  ]\n}\n```\n\n### Migration Flow\n\n1. **First Init** (`isInit = false`):\n   - Schema is created from `snapshot.json`\n   - Default data is inserted from `default-data.json`\n   - System sets `isInit = true`\n\n2. **Subsequent Starts** (`isInit = true`):\n   - Schema is synced from `snapshot.json` (auto-add new columns/relations)\n   - Schema migrations run from `snapshot-migration.json` (remove/modify)\n   - Data migrations run from `data-migration.json`\n\n### Supported Databases\n\n- **SQL**: MySQL, PostgreSQL, MariaDB\n- **NoSQL**: MongoDB\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fenfyra%2Fserver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fenfyra%2Fserver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fenfyra%2Fserver/lists"}