A robust and scalable REST API backend system for Server-Driven UI that serves UI definitions as JSON to mobile applications. Built with Express.js, MongoDB, and Cloudflare R2 storage.
- Multi-App Support: Manage multiple mobile applications with separate UI definitions
- Screen Management: CRUD operations for screen UI JSON definitions
- Secure Storage: JSON files stored in Cloudflare R2 with metadata in MongoDB
- API Key Authentication: Secure access control for mobile apps
- Developer Management: Separate developer accounts with app ownership
- Malware Scanning: Built-in security scanning for uploaded JSON content
- Rate Limiting: Protection against abuse and DDoS attacks
- Scalable Architecture: Separation of concerns with clean MVC pattern
- Prerequisites
- Installation
- Configuration
- Project Structure
- API Documentation
- Usage Examples
- Security
- Deployment
- Node.js (v16 or higher)
- MongoDB (v5.0 or higher)
- Cloudflare R2 account with:
- Account ID
- Access Key ID
- Secret Access Key
- Bucket created
Clone the repository:
cd ketoy-nodejsInstall dependencies:
npm install
Set up environment variables:
cp .env.example .env
Edit
.envwith your actual credentials.Start the server:
# Development mode with auto-reload npm run dev # Production mode npm start
Create a .env file in the root directory with the following variables:
# Server ConfigurationPORT=3000NODE_ENV=development# MongoDB ConfigurationMONGODB_URI=mongodb://localhost:27017/sdui_backend# Cloudflare R2 ConfigurationR2_ACCOUNT_ID=your_cloudflare_account_idR2_ACCESS_KEY_ID=your_r2_access_key_idR2_SECRET_ACCESS_KEY=your_r2_secret_access_keyR2_BUCKET_NAME=sdui-json-filesR2_ENDPOINT=https://your-account-id.r2.cloudflarestorage.com# SecurityAPI_SECRET=your_super_secret_key_for_generating_api_keysJWT_SECRET=your_jwt_secret_key# Rate LimitingRATE_LIMIT_WINDOW_MS=900000RATE_LIMIT_MAX_REQUESTS=100# CORS ConfigurationCORS_ORIGIN=*- Log in to your Cloudflare dashboard
- Navigate to R2 Storage
- Create a new bucket (e.g.,
sdui-json-files) - Generate API tokens with read/write permissions
- Copy your Account ID and API credentials
ketoy-nodejs/
├── config/
│ ├── config.js # Configuration settings
│ └── database.js # MongoDB connection
├── controllers/
│ ├── developerController.js # Developer management logic
│ ├── appController.js # App management logic
│ └── screenController.js # Screen management logic
├── middleware/
│ ├── auth.js # Authentication middleware
│ ├── validation.js # Request validation
│ ├── security.js # JSON scanning & security
│ ├── errorHandler.js # Error handling
│ └── index.js # Middleware exports
├── models/
│ ├── Developer.js # Developer schema
│ ├── App.js # App schema
│ ├── Screen.js # Screen schema
│ └── index.js # Model exports
├── routes/
│ ├── developerRoutes.js # Developer endpoints
│ ├── appRoutes.js # App endpoints
│ ├── screenRoutes.js # Screen endpoints
│ └── index.js # Route aggregation
├── services/
│ └── r2Storage.js # Cloudflare R2 service
├── .env.example # Environment template
├── .gitignore # Git ignore rules
├── index.js # Server entry point
├── package.json # Dependencies
└── README.md # Documentation
http://localhost:3000/api
- Sign in on the client using Firebase Authentication.
- Send Firebase ID token to
POST /api/developers/authenticatewithemailanduserId. - Use returned backend JWT in all developer APIs:
Authorization: Bearer YOUR_DEVELOPER_JWT
Include both headers or query parameters:
x-api-keyorapi_key: Your app's API keyx-package-nameorpackage_name: Your app's package name
POST /api/developers/authenticateAuthorization: Bearer FIREBASE_ID_TOKENContent-Type: application/json
{
"email": "developer@example.com",
"userId": "firebase_uid_here"
}Response:
{
"success": true,
"message": "Developer authenticated successfully",
"data": {
"developer": {
"id": "64a1b2c3d4e5f6g7h8i9j0k1",
"firebaseUid": "firebase_uid_here",
"email": "developer@example.com",
"name": "",
"contactDetails": {},
"createdAt": "2026-02-10T10:00:00.000Z"
},
"token": "jwt_token_for_backend_apis"
}
}GET /api/developers/profileAuthorization: Bearer YOUR_DEVELOPER_JWTPUT /api/developers/profileAuthorization: Bearer YOUR_DEVELOPER_JWTContent-Type: application/json
{
"name": "John Smith"
}POST /api/developers/contact-detailsAuthorization: Bearer YOUR_DEVELOPER_JWTContent-Type: application/json
{
"contactDetails": {
"phone": "+1234567890"
}
}GET /api/developers/contact-details/statusAuthorization: Bearer YOUR_DEVELOPER_JWTPOST /api/apps/registerAuthorization: Bearer YOUR_DEVELOPER_JWTContent-Type: application/json
{
"packageName": "com.example.myapp",
"appName": "My Awesome App",
"description": "A great mobile app",
"metadata": {
"version": "1.0.0",
"platform": "android"
}
}Response:
{
"success": true,
"message": "App registered successfully",
"data": {
"app": {
"id": "64a1b2c3d4e5f6g7h8i9j0k2",
"packageName": "com.example.myapp",
"appName": "My Awesome App",
"apiKey": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6",
"r2FolderPath": "apps/com.example.myapp",
"createdAt": "2026-02-10T10:00:00.000Z"
}
},
"important": "Please save the API key securely. It will not be shown again."
}GET /api/apps?page=1&limit=10&search=myappAuthorization: Bearer YOUR_DEVELOPER_JWTGET /api/apps/com.example.myappAuthorization: Bearer YOUR_DEVELOPER_JWTPUT /api/apps/com.example.myappAuthorization: Bearer YOUR_DEVELOPER_JWTContent-Type: application/json
{
"appName": "My Updated App",
"isActive": true
}POST /api/apps/com.example.myapp/regenerate-keyAuthorization: Bearer YOUR_DEVELOPER_JWTGET /api/apps/com.example.myapp/statsAuthorization: Bearer YOUR_DEVELOPER_JWTDELETE /api/apps/com.example.myappAuthorization: Bearer YOUR_DEVELOPER_JWTPOST /api/screens/com.example.myapp/uploadAuthorization: Bearer YOUR_DEVELOPER_JWTContent-Type: application/json
{
"screenName": "home_screen",
"displayName": "Home Screen",
"description": "Main home screen of the app",
"version": "1.0.0",
"jsonContent": "{\"type\":\"Column\",\"children\":[{\"type\":\"Text\",\"text\":\"Hello World\"}]}",
"metadata": {
"category": "main",
"tags": ["home", "main"]
}
}Response:
{
"success": true,
"message": "Screen uploaded successfully",
"data": {
"screen": {
"id": "64a1b2c3d4e5f6g7h8i9j0k3",
"screenName": "home_screen",
"displayName": "Home Screen",
"jsonFilePath": "apps/com.example.myapp/home_screen.json",
"version": "1.0.0",
"fileSize": 1024,
"createdAt": "2026-02-10T10:00:00.000Z"
}
}
}GET /api/screens/com.example.myapp?page=1&limit=20&search=home&isActive=trueAuthorization: Bearer YOUR_DEVELOPER_JWTGET /api/screens/com.example.myapp/home_screen/details?includeJson=trueAuthorization: Bearer YOUR_DEVELOPER_JWTPUT /api/screens/com.example.myapp/home_screenAuthorization: Bearer YOUR_DEVELOPER_JWTContent-Type: application/json
{
"displayName": "Updated Home Screen",
"jsonContent": "{\"type\":\"Column\",\"children\":[{\"type\":\"Text\",\"text\":\"Updated!\"}]}",
"version": "1.0.1"
}DELETE /api/screens/com.example.myapp/home_screenAuthorization: Bearer YOUR_DEVELOPER_JWTThis is the primary endpoint used by mobile applications to fetch UI definitions.
GET /api/v1/screen?screen_name=home_screenx-api-key: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6x-package-name: com.example.myappAlternative (Query Parameters):
GET /api/v1/screen?screen_name=home_screen&api_key=YOUR_API_KEY&package_name=com.example.myappResponse:
{
"success": true,
"data": {
"screenName": "home_screen",
"version": "1.0.0",
"ui": {
"type": "Column",
"children": [
{
"type": "Text",
"text": "Hello World"
}
]
}
}
}- Register as a developer:
curl -X POST http://localhost:3000/api/developers/authenticate \
-H "Content-Type: application/json" \
-d '{ "email": "dev@example.com", "name": "John Doe" }'- Register your app:
curl -X POST http://localhost:3000/api/apps/register \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_DEVELOPER_JWT" \
-d '{ "packageName": "com.example.app", "appName": "My App" }'Save the returned API key!
- Upload a screen:
curl -X POST http://localhost:3000/api/screens/com.example.app/upload \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_DEVELOPER_JWT" \
-d '{ "screenName": "home", "jsonContent": "{\"type\":\"Text\",\"text\":\"Hello\"}" }'- Fetch from mobile app:
curl -X GET "http://localhost:3000/api/v1/screen?screen_name=home" \
-H "x-api-key: YOUR_API_KEY" \
-H "x-package-name: com.example.app"- Each app gets a unique API key
- Keys are hashed and stored securely
- Keys can be regenerated if compromised
Automatically scans uploaded JSON for:
- Script tags and JavaScript code
- Event handlers (onclick, onerror, etc.)
- eval() and execScript calls
- Path traversal attempts
- System file access patterns
- Excessive nesting (default max: 25 levels, configurable)
- Large payloads (default max: 10MB, configurable)
Configuration:
MAX_JSON_DEPTH=25# Maximum nesting depthMAX_JSON_SIZE=10485760# Maximum size in bytes (10MB)- 100 requests per 15 minutes per IP
- Configurable via environment variables
- All inputs validated using express-validator
- Type checking and sanitization
- SQL/NoSQL injection prevention
- Developers can only access their own apps
- Package name verification for mobile apps
- Active status checks
Environment variables:
- Set
NODE_ENV=production - Use strong secrets for
API_SECRETandJWT_SECRET - Configure proper MongoDB URI
- Set up Cloudflare R2 credentials
- Set
Security:
- Enable HTTPS
- Configure CORS properly
- Set up firewall rules
- Use environment-specific rate limits
Performance:
- Enable MongoDB indexes (already configured)
- Configure connection pooling
- Set up caching if needed
- Monitor R2 usage
Monitoring:
- Set up logging (Winston, Loggly, etc.)
- Monitor server health
- Track API usage
- Set up alerts
Create a Dockerfile:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]Build and run:
docker build -t sdui-backend .
docker run -p 3000:3000 --env-file .env sdui-backendcurl http://localhost:3000/api/health# Should return 401
curl -X GET "http://localhost:3000/api/v1/screen?screen_name=test"# Should work
curl -X GET "http://localhost:3000/api/v1/screen?screen_name=test" \
-H "x-api-key: YOUR_API_KEY" \
-H "x-package-name: com.example.app"The server logs important events:
- MongoDB connection status
- API requests (via Morgan)
- File uploads to R2
- Security scan results
- Error traces
In production, consider integrating:
- Winston for structured logging
- PM2 for process management
- New Relic or DataDog for monitoring
- Fork the repository
- Create your feature branch
- Commit your changes
- Push to the branch
- Create a Pull Request
ISC License - See LICENSE file for details
For issues, questions, or contributions:
- Create an issue on GitHub
- Contact: Developer String
- Add JWT authentication for developers
- Implement versioning for screen JSON
- Add webhook notifications for screen updates
- Create admin dashboard
- Add analytics for screen usage
- Implement JSON diff for version comparison
- Add GraphQL support
- Create SDK for mobile apps
Built with ❤️ by Developer String