@rapidd/core 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +71 -0
- package/.env.example +70 -0
- package/.gitignore +11 -0
- package/LICENSE +15 -0
- package/README.md +231 -0
- package/bin/cli.js +145 -0
- package/config/app.json +166 -0
- package/config/rate-limit.json +12 -0
- package/dist/main.js +26 -0
- package/dockerfile +57 -0
- package/locales/ar_SA.json +179 -0
- package/locales/de_DE.json +179 -0
- package/locales/en_US.json +180 -0
- package/locales/es_ES.json +179 -0
- package/locales/fr_FR.json +179 -0
- package/locales/it_IT.json +179 -0
- package/locales/ja_JP.json +179 -0
- package/locales/pt_BR.json +179 -0
- package/locales/ru_RU.json +179 -0
- package/locales/tr_TR.json +179 -0
- package/main.ts +25 -0
- package/package.json +126 -0
- package/prisma/schema.prisma +9 -0
- package/prisma.config.ts +12 -0
- package/public/static/favicon.ico +0 -0
- package/public/static/image/logo.png +0 -0
- package/routes/api/v1/index.ts +113 -0
- package/src/app.ts +197 -0
- package/src/auth/Auth.ts +446 -0
- package/src/auth/stores/ISessionStore.ts +19 -0
- package/src/auth/stores/MemoryStore.ts +70 -0
- package/src/auth/stores/RedisStore.ts +92 -0
- package/src/auth/stores/index.ts +149 -0
- package/src/config/acl.ts +9 -0
- package/src/config/rls.ts +38 -0
- package/src/core/dmmf.ts +226 -0
- package/src/core/env.ts +183 -0
- package/src/core/errors.ts +87 -0
- package/src/core/i18n.ts +144 -0
- package/src/core/middleware.ts +123 -0
- package/src/core/prisma.ts +236 -0
- package/src/index.ts +112 -0
- package/src/middleware/model.ts +61 -0
- package/src/orm/Model.ts +881 -0
- package/src/orm/QueryBuilder.ts +2078 -0
- package/src/plugins/auth.ts +162 -0
- package/src/plugins/language.ts +79 -0
- package/src/plugins/rateLimit.ts +210 -0
- package/src/plugins/response.ts +80 -0
- package/src/plugins/rls.ts +51 -0
- package/src/plugins/security.ts +23 -0
- package/src/plugins/upload.ts +299 -0
- package/src/types.ts +308 -0
- package/src/utils/ApiClient.ts +526 -0
- package/src/utils/Mailer.ts +348 -0
- package/src/utils/index.ts +25 -0
- package/templates/email/example.ejs +17 -0
- package/templates/layouts/email.ejs +35 -0
- package/tsconfig.json +33 -0
package/.dockerignore
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules
|
|
3
|
+
npm-debug.log*
|
|
4
|
+
yarn-debug.log*
|
|
5
|
+
yarn-error.log*
|
|
6
|
+
|
|
7
|
+
# Testing and development
|
|
8
|
+
__test__
|
|
9
|
+
*.test.ts
|
|
10
|
+
*.test.js
|
|
11
|
+
*.spec.ts
|
|
12
|
+
*.spec.js
|
|
13
|
+
jest.setup.*
|
|
14
|
+
coverage
|
|
15
|
+
|
|
16
|
+
# Environment files
|
|
17
|
+
.env
|
|
18
|
+
.env.*
|
|
19
|
+
!.env.example
|
|
20
|
+
|
|
21
|
+
# Version control and CI/CD
|
|
22
|
+
.git
|
|
23
|
+
.gitignore
|
|
24
|
+
.github
|
|
25
|
+
.gitlab-ci.yml
|
|
26
|
+
|
|
27
|
+
# Documentation
|
|
28
|
+
*.md
|
|
29
|
+
README*
|
|
30
|
+
CHANGELOG*
|
|
31
|
+
LICENSE*
|
|
32
|
+
|
|
33
|
+
# Docker files
|
|
34
|
+
Dockerfile*
|
|
35
|
+
dockerfile*
|
|
36
|
+
docker-compose*
|
|
37
|
+
compose.yml
|
|
38
|
+
.dockerignore
|
|
39
|
+
|
|
40
|
+
# IDE and editor files
|
|
41
|
+
.vscode
|
|
42
|
+
.idea
|
|
43
|
+
.claude
|
|
44
|
+
*.swp
|
|
45
|
+
*.swo
|
|
46
|
+
*~
|
|
47
|
+
|
|
48
|
+
# OS files
|
|
49
|
+
.DS_Store
|
|
50
|
+
Thumbs.db
|
|
51
|
+
|
|
52
|
+
# Logs and temporary files
|
|
53
|
+
logs
|
|
54
|
+
*.log
|
|
55
|
+
tmp
|
|
56
|
+
temp
|
|
57
|
+
*.tmp
|
|
58
|
+
|
|
59
|
+
# Build artifacts (rebuilt in Docker)
|
|
60
|
+
dist
|
|
61
|
+
|
|
62
|
+
# Prisma client (regenerated in Docker)
|
|
63
|
+
prisma/client
|
|
64
|
+
|
|
65
|
+
# TypeScript config for tests
|
|
66
|
+
tsconfig.test.json
|
|
67
|
+
|
|
68
|
+
# Other
|
|
69
|
+
*.orig
|
|
70
|
+
*.backup
|
|
71
|
+
data/
|
package/.env.example
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
NODE_ENV=development
|
|
2
|
+
|
|
3
|
+
# ── Database (REQUIRED) ────────────────────────────────
|
|
4
|
+
DATABASE_URL="postgresql://user:password@localhost:5432/database?schema=public"
|
|
5
|
+
# Admin connection (bypasses RLS); defaults to DATABASE_URL
|
|
6
|
+
DATABASE_URL_ADMIN=
|
|
7
|
+
# Auto-detected from URL; set to "postgresql" or "mysql" to override
|
|
8
|
+
DATABASE_PROVIDER=
|
|
9
|
+
|
|
10
|
+
# ── Server ─────────────────────────────────────────────
|
|
11
|
+
HOST=0.0.0.0
|
|
12
|
+
PORT=3000
|
|
13
|
+
DOMAIN=localhost
|
|
14
|
+
FRONTEND_DOMAIN=frontend.domain
|
|
15
|
+
ALLOWED_ORIGINS=frontend.domain
|
|
16
|
+
COOKIE_SECRET=your-cookie-secret-here
|
|
17
|
+
# true/false; defaults to true in production
|
|
18
|
+
TRUST_PROXY=
|
|
19
|
+
|
|
20
|
+
# ── Authentication (auto-configured) ───────────────────
|
|
21
|
+
# Auth is auto-enabled when a user table is detected in the Prisma schema.
|
|
22
|
+
# Identifier fields and password field are auto-detected from the schema.
|
|
23
|
+
# JWT secrets are auto-generated if not set (won't persist across restarts).
|
|
24
|
+
JWT_SECRET=
|
|
25
|
+
JWT_REFRESH_SECRET=
|
|
26
|
+
# redis or memory
|
|
27
|
+
AUTH_SESSION_STORAGE=redis
|
|
28
|
+
# Session TTL in seconds (default: 24h)
|
|
29
|
+
AUTH_SESSION_TTL=86400
|
|
30
|
+
# bcrypt salt rounds
|
|
31
|
+
AUTH_SALT_ROUNDS=10
|
|
32
|
+
# JWT access token expiry
|
|
33
|
+
AUTH_ACCESS_TOKEN_EXPIRY=1d
|
|
34
|
+
# JWT refresh token expiry
|
|
35
|
+
AUTH_REFRESH_TOKEN_EXPIRY=30d
|
|
36
|
+
# Auto-detected; set to override (e.g., "accounts")
|
|
37
|
+
DB_USER_TABLE=
|
|
38
|
+
# Auto-detected; set to override (e.g., "hash")
|
|
39
|
+
DB_USER_PASSWORD_FIELD=
|
|
40
|
+
# Auto-detected from unique string fields; comma-separated (default: email)
|
|
41
|
+
DB_USER_IDENTIFIER_FIELDS=
|
|
42
|
+
# Comma-separated: bearer, basic, cookie, header (default: bearer)
|
|
43
|
+
AUTH_STRATEGIES=bearer
|
|
44
|
+
# Cookie name for cookie strategy (default: token)
|
|
45
|
+
AUTH_COOKIE_NAME=token
|
|
46
|
+
# Header name for header strategy (default: X-Auth-Token)
|
|
47
|
+
AUTH_CUSTOM_HEADER=X-Auth-Token
|
|
48
|
+
|
|
49
|
+
# ── API Settings ───────────────────────────────────────
|
|
50
|
+
# Max results per query
|
|
51
|
+
API_RESULT_LIMIT=500
|
|
52
|
+
# Rate limit window in ms (default: 15min)
|
|
53
|
+
RATE_LIMIT_WINDOW_MS=900000
|
|
54
|
+
# Max requests per window
|
|
55
|
+
RATE_LIMIT_MAX_REQUESTS=100
|
|
56
|
+
|
|
57
|
+
# ── Redis ──────────────────────────────────────────────
|
|
58
|
+
REDIS_HOST=localhost
|
|
59
|
+
REDIS_PORT=6379
|
|
60
|
+
REDIS_PASSWORD=
|
|
61
|
+
REDIS_DB_RATE_LIMIT=0
|
|
62
|
+
REDIS_DB_AUTH=1
|
|
63
|
+
|
|
64
|
+
# ── Row-Level Security ─────────────────────────────────
|
|
65
|
+
# RLS is auto-enabled for PostgreSQL and disabled for MySQL.
|
|
66
|
+
# true/false; override auto-detection
|
|
67
|
+
RLS_ENABLED=
|
|
68
|
+
# Namespace prefix for SQL session variables (default: app)
|
|
69
|
+
RLS_NAMESPACE=app
|
|
70
|
+
# Configure RLS variables in src/config/rls.ts
|
package/.gitignore
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Mert Dalbudak
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
14
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
<img width="64" height="64" alt="logo" src="https://github.com/user-attachments/assets/706dd13b-212c-4076-b4d7-94dec4001a06" />
|
|
2
|
+
|
|
3
|
+
# Rapidd
|
|
4
|
+
|
|
5
|
+
Code-first REST API framework for TypeScript. Database in, API out.
|
|
6
|
+
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://fastify.dev/)
|
|
9
|
+
[](https://www.prisma.io/)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
[](https://github.com/MertDalbudak/rapidd/actions/workflows/ci.yml)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Why Rapidd
|
|
16
|
+
|
|
17
|
+
Rapidd generates a fully-featured REST API from your database schema — then gets out of your way. It's not a scaffolder that dumps code you'll rewrite. It's not a hosted service between you and your data. It's a framework you own, extend, and deploy anywhere.
|
|
18
|
+
|
|
19
|
+
**Unlike Hasura or Supabase**, you get a full TypeScript codebase — no vendor lock-in, no managed service dependency. **Unlike PostgREST**, you get auth, ACL, middleware hooks, and utilities built in. **Unlike Strapi**, it's schema-first via Prisma, not UI-driven.
|
|
20
|
+
|
|
21
|
+
- **Zero to API in 3 commands** — CRUD endpoints with filtering, pagination, relations, and field selection
|
|
22
|
+
- **Convention over configuration** — auto-detects auth tables, password fields, DB provider, and RLS support. Every default overridable
|
|
23
|
+
- **Production-grade from day one** — security headers, JWT with refresh rotation, row-level security, per-model ACL, rate limiting
|
|
24
|
+
- **Batteries included** — HTTP client, SMTP mailer with templates, file uploads, rate limiting, i18n across 10 languages
|
|
25
|
+
- **Fully extensible** — before/after middleware on every CRUD operation, custom routes alongside generated ones
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @rapidd/build
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```env
|
|
36
|
+
DATABASE_URL="postgresql://user:pass@localhost:5432/mydb"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx prisma db pull # introspect existing database
|
|
41
|
+
npx rapidd build # generate models, routes & ACL scaffold
|
|
42
|
+
npm run dev # http://localhost:3000
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Every table gets full CRUD endpoints. Auth is enabled automatically when a user table is detected. Every auto-detected value — auth fields, password hashing, JWT secrets, session store — is overridable via env vars. See [`.env.example`](.env.example) for the full list.
|
|
46
|
+
|
|
47
|
+
> 📖 **[Getting Started guide →](https://github.com/MertDalbudak/rapidd/wiki/Getting-Started)** — full walkthrough with project structure
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Features
|
|
52
|
+
|
|
53
|
+
| Feature | PostgreSQL | MySQL/MariaDB |
|
|
54
|
+
|---------|:----------:|:-------------:|
|
|
55
|
+
| CRUD API generation | ✓ | ✓ |
|
|
56
|
+
| Query filtering (20+ operators) | ✓ | ✓ |
|
|
57
|
+
| Relations & deep includes | ✓ | ✓ |
|
|
58
|
+
| Field selection | ✓ | ✓ |
|
|
59
|
+
| JWT authentication (4 strategies) | ✓ | ✓ |
|
|
60
|
+
| Per-model ACL | ✓ | ✓ |
|
|
61
|
+
| Row-Level Security (database-enforced) | ✓ | — |
|
|
62
|
+
| Rate limiting (Redis + memory fallback) | ✓ | ✓ |
|
|
63
|
+
| File uploads with MIME validation | ✓ | ✓ |
|
|
64
|
+
| SMTP mailer with EJS templates | ✓ | ✓ |
|
|
65
|
+
| Config-driven HTTP client | ✓ | ✓ |
|
|
66
|
+
| i18n (10 languages) | ✓ | ✓ |
|
|
67
|
+
| Security headers (HSTS, CSP, etc.) | ✓ | ✓ |
|
|
68
|
+
|
|
69
|
+
> **MySQL note:** ACL provides application-level access control for all databases. RLS adds database-enforced row filtering as a second layer (PostgreSQL-only). For MySQL, ACL is your primary access control mechanism and covers most use cases. See the **[Access Control wiki](https://github.com/MertDalbudak/rapidd/wiki/Access-Control-(ACL))** for details.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Query API
|
|
74
|
+
|
|
75
|
+
All generated endpoints support filtering, relations, field selection, sorting, and pagination.
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
GET /api/v1/posts?filter=status=active,title=%typescript%
|
|
79
|
+
GET /api/v1/posts?filter=createdAt=after:2025-01-01,views=gte:100
|
|
80
|
+
GET /api/v1/posts?include=author,comments.user&fields=id,title,author.name
|
|
81
|
+
GET /api/v1/posts?sortBy=createdAt&sortOrder=desc&limit=10&offset=20
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
20+ filter operators for strings, numbers, dates, arrays, nulls, and nested relation fields. Responses include pagination metadata with `total`, `count`, `limit`, `offset`, and `hasMore`.
|
|
85
|
+
|
|
86
|
+
> 📖 **[Query API wiki →](https://github.com/MertDalbudak/rapidd/wiki/Query-API)** — all operators, composite PKs, relation filtering
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Authentication
|
|
91
|
+
|
|
92
|
+
Auto-enabled when a user table is detected.
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
POST /auth/login { "user": "john@example.com", "password": "..." }
|
|
96
|
+
POST /auth/logout Authorization: Bearer <token>
|
|
97
|
+
POST /auth/refresh { "refreshToken": "..." }
|
|
98
|
+
GET /auth/me Authorization: Bearer <token>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Four strategies — **bearer** (default), **basic**, **cookie**, and **custom header** — configurable per-route. Multi-identifier login lets users authenticate with any unique field (email, username, phone) in a single endpoint.
|
|
102
|
+
|
|
103
|
+
⚠️ **Production:** `JWT_SECRET` and `JWT_REFRESH_SECRET` must be set explicitly. The server refuses to start without them to prevent session invalidation on restart.
|
|
104
|
+
|
|
105
|
+
> 📖 **[Authentication wiki →](https://github.com/MertDalbudak/rapidd/wiki/Authentication)** — session stores, route protection, per-endpoint strategy overrides
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Access Control
|
|
110
|
+
|
|
111
|
+
Define per-model rules in `src/config/acl.ts`. Enforced on every CRUD operation, relation include, and field selection.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
const acl: AclConfig = {
|
|
115
|
+
model: {
|
|
116
|
+
posts: {
|
|
117
|
+
canCreate: (user, data) => user.role !== 'GUEST',
|
|
118
|
+
getAccessFilter: (user) => user.role === 'ADMIN' ? {} : { authorId: user.id },
|
|
119
|
+
getUpdateFilter: (user) => user.role === 'ADMIN' ? {} : { authorId: user.id },
|
|
120
|
+
getDeleteFilter: (user) => user.role === 'ADMIN' ? {} : false,
|
|
121
|
+
getOmitFields: (user) => user.role !== 'ADMIN' ? ['internalNotes'] : [],
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Return `{}` for full access, a filter object to scope records, or `false` to deny.
|
|
128
|
+
|
|
129
|
+
> 📖 **[Access Control wiki →](https://github.com/MertDalbudak/rapidd/wiki/Access-Control-(ACL))** — all 5 ACL methods, relation ACL, 404 vs 403 distinction
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Model Middleware
|
|
134
|
+
|
|
135
|
+
Hook into any CRUD operation before or after execution.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
// Auto-timestamps
|
|
139
|
+
Model.middleware.use('before', 'create', async (ctx) => {
|
|
140
|
+
ctx.data.createdAt = new Date();
|
|
141
|
+
ctx.data.createdBy = ctx.user?.id;
|
|
142
|
+
return ctx;
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Soft deletes (scoped to a specific model)
|
|
146
|
+
Model.middleware.use('before', 'delete', async (ctx) => {
|
|
147
|
+
ctx.softDelete = true;
|
|
148
|
+
ctx.data = { deletedAt: new Date(), deletedBy: ctx.user?.id };
|
|
149
|
+
return ctx;
|
|
150
|
+
}, 'posts');
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Supports `create`, `update`, `upsert`, `upsertMany`, `delete`, `get`, `getMany`, and `count`. Middleware can abort operations, modify data, and short-circuit with cached results.
|
|
154
|
+
|
|
155
|
+
> 📖 **[Model Middleware wiki →](https://github.com/MertDalbudak/rapidd/wiki/Model-Middleware)** — all hooks, context object, patterns (soft delete, validation, caching)
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Row-Level Security
|
|
160
|
+
|
|
161
|
+
Auto-enabled for PostgreSQL. Define which variables to inject in `src/config/rls.ts`:
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// src/config/rls.ts
|
|
165
|
+
const rlsContext: RlsContextFn = (request) => ({
|
|
166
|
+
current_user_id: request.user?.id ?? null,
|
|
167
|
+
current_tenant_id: request.headers['x-tenant-id'] ?? null,
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
```sql
|
|
172
|
+
CREATE POLICY tenant_isolation ON orders
|
|
173
|
+
USING (tenant_id = current_setting('app.current_tenant_id')::int);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
> 📖 **[Row-Level Security wiki →](https://github.com/MertDalbudak/rapidd/wiki/Row%E2%80%90Level-Security-(RLS))** — policy examples, RLS vs ACL comparison
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Built-in Utilities
|
|
181
|
+
|
|
182
|
+
| Utility | Description | Docs |
|
|
183
|
+
|---------|-------------|------|
|
|
184
|
+
| **ApiClient** | Config-driven HTTP client with Bearer, Basic, API Key, and OAuth2 auth. Automatic token caching, retries, and fluent builder. | [Wiki →](https://github.com/MertDalbudak/rapidd/wiki/ApiClient) |
|
|
185
|
+
| **Mailer** | SMTP email with EJS template rendering, layout wrappers, i18n support, batch sending, and attachments. | [Wiki →](https://github.com/MertDalbudak/rapidd/wiki/Mailer) |
|
|
186
|
+
| **File Uploads** | Multipart uploads with MIME validation, size limits, and type presets (`images`, `documents`, etc.). | [Wiki →](https://github.com/MertDalbudak/rapidd/wiki/File-Uploads) |
|
|
187
|
+
| **Rate Limiting** | Redis-backed with automatic memory fallback. Per-path configuration via `config/rate-limit.json`. | [Wiki →](https://github.com/MertDalbudak/rapidd/wiki/Rate-Limiting) |
|
|
188
|
+
| **i18n** | 10 languages included. Auto-detected from `Accept-Language` header. Parameter interpolation in error messages. | [Wiki →](https://github.com/MertDalbudak/rapidd/wiki/Internationalization-(i18n)) |
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Production & Deployment
|
|
193
|
+
|
|
194
|
+
```env
|
|
195
|
+
NODE_ENV=production
|
|
196
|
+
JWT_SECRET=your-secret-here # Required — server won't start without it
|
|
197
|
+
JWT_REFRESH_SECRET=your-refresh-secret
|
|
198
|
+
ALLOWED_ORIGINS=yourdomain.com
|
|
199
|
+
TRUST_PROXY=true
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
npm run build && npm start
|
|
204
|
+
|
|
205
|
+
# or Docker
|
|
206
|
+
docker build -t rapidd . && docker run -p 3000:3000 --env-file .env rapidd
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Security defaults in production:** HSTS, Content-Security-Policy, X-Content-Type-Options, Referrer-Policy, and CORS with explicit origin whitelisting — all enabled automatically.
|
|
210
|
+
|
|
211
|
+
> 📖 **[Deployment wiki →](https://github.com/MertDalbudak/rapidd/wiki/Deployment-&-Production)** — Docker Compose, nginx reverse proxy, production checklist, horizontal scaling
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Documentation
|
|
216
|
+
|
|
217
|
+
Full documentation: **[github.com/MertDalbudak/rapidd/wiki](https://github.com/MertDalbudak/rapidd/wiki)**
|
|
218
|
+
|
|
219
|
+
[Getting Started](https://github.com/MertDalbudak/rapidd/wiki/Getting-Started) · [Configuration](https://github.com/MertDalbudak/rapidd/wiki/Configuration) · [Query API](https://github.com/MertDalbudak/rapidd/wiki/Query-API) · [Authentication](https://github.com/MertDalbudak/rapidd/wiki/Authentication) · [Access Control](https://github.com/MertDalbudak/rapidd/wiki/Access-Control-(ACL)) · [Model Middleware](https://github.com/MertDalbudak/rapidd/wiki/Model-Middleware) · [Row-Level Security](https://github.com/MertDalbudak/rapidd/wiki/Row%E2%80%90Level-Security-(RLS)) · [ApiClient](https://github.com/MertDalbudak/rapidd/wiki/ApiClient) · [Mailer](https://github.com/MertDalbudak/rapidd/wiki/Mailer) · [File Uploads](https://github.com/MertDalbudak/rapidd/wiki/File-Uploads) · [Rate Limiting](https://github.com/MertDalbudak/rapidd/wiki/Rate-Limiting) · [i18n](https://github.com/MertDalbudak/rapidd/wiki/Internationalization-(i18n)) · [Deployment](https://github.com/MertDalbudak/rapidd/wiki/Deployment-&-Production)
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Contributing
|
|
224
|
+
|
|
225
|
+
Issues and pull requests are welcome. If you find a bug or have a feature request, [open an issue](https://github.com/MertDalbudak/rapidd/issues).
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
[ISC](LICENSE)
|
|
230
|
+
|
|
231
|
+
Built by [Mert Dalbudak](https://github.com/MertDalbudak)
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const COMMANDS = { 'create-project': createProject };
|
|
7
|
+
|
|
8
|
+
const args = process.argv.slice(2);
|
|
9
|
+
const command = args[0];
|
|
10
|
+
|
|
11
|
+
if (!command || !COMMANDS[command]) {
|
|
12
|
+
console.log('Usage: npx rapidd <command>\n');
|
|
13
|
+
console.log('Commands:');
|
|
14
|
+
console.log(' create-project Scaffold a new Rapidd project in the current directory');
|
|
15
|
+
process.exit(command ? 1 : 0);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
COMMANDS[command](args.slice(1));
|
|
19
|
+
|
|
20
|
+
function createProject() {
|
|
21
|
+
const targetDir = process.cwd();
|
|
22
|
+
|
|
23
|
+
// Safety check — don't overwrite an existing project
|
|
24
|
+
if (fs.existsSync(path.join(targetDir, 'src')) || fs.existsSync(path.join(targetDir, 'main.ts'))) {
|
|
25
|
+
console.error('Error: Current directory already contains a project (src/ or main.ts found).');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
30
|
+
const projectName = path.basename(targetDir);
|
|
31
|
+
|
|
32
|
+
const SKIP = new Set([
|
|
33
|
+
'node_modules', 'dist', '__test__', '.github', '.claude', 'wiki',
|
|
34
|
+
'bin', '.git', 'package-lock.json', '.env', 'prisma/client',
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
const FILES = [
|
|
38
|
+
'src/',
|
|
39
|
+
'config/',
|
|
40
|
+
'locales/',
|
|
41
|
+
'routes/',
|
|
42
|
+
'templates/',
|
|
43
|
+
'public/',
|
|
44
|
+
'prisma/schema.prisma',
|
|
45
|
+
'prisma.config.ts',
|
|
46
|
+
'main.ts',
|
|
47
|
+
'tsconfig.json',
|
|
48
|
+
'.env.example',
|
|
49
|
+
'.gitignore',
|
|
50
|
+
'.dockerignore',
|
|
51
|
+
'dockerfile',
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
console.log(`\nCreating project in ${targetDir}...\n`);
|
|
55
|
+
|
|
56
|
+
for (const entry of FILES) {
|
|
57
|
+
const src = path.join(packageRoot, entry);
|
|
58
|
+
if (!fs.existsSync(src)) continue;
|
|
59
|
+
|
|
60
|
+
const dest = path.join(targetDir, entry);
|
|
61
|
+
|
|
62
|
+
if (entry.endsWith('/')) {
|
|
63
|
+
copyDir(src, dest, SKIP);
|
|
64
|
+
} else {
|
|
65
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
66
|
+
fs.copyFileSync(src, dest);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Generate a fresh package.json for the new project
|
|
71
|
+
const pkg = {
|
|
72
|
+
name: projectName,
|
|
73
|
+
version: '1.0.0',
|
|
74
|
+
private: true,
|
|
75
|
+
scripts: {
|
|
76
|
+
start: 'node dist/main.js',
|
|
77
|
+
dev: 'tsx watch main.ts',
|
|
78
|
+
build: 'tsc',
|
|
79
|
+
},
|
|
80
|
+
engines: { node: '>=24.0.0' },
|
|
81
|
+
dependencies: {
|
|
82
|
+
'@fastify/cookie': '^11.0.2',
|
|
83
|
+
'@fastify/cors': '^11.0.0',
|
|
84
|
+
'@fastify/formbody': '^8.0.2',
|
|
85
|
+
'@fastify/multipart': '^9.4.0',
|
|
86
|
+
'@fastify/static': '^9.0.0',
|
|
87
|
+
'@prisma/adapter-mariadb': '^7.0.1',
|
|
88
|
+
'@prisma/adapter-pg': '^7.0.1',
|
|
89
|
+
'@prisma/client': '^7.0.1',
|
|
90
|
+
'@prisma/internals': '^7.0.1',
|
|
91
|
+
'bcrypt': '^6.0.0',
|
|
92
|
+
'dotenv': '^17.3.1',
|
|
93
|
+
'ejs': '^4.0.1',
|
|
94
|
+
'fastify': '^5.2.1',
|
|
95
|
+
'fastify-plugin': '^5.0.1',
|
|
96
|
+
'ioredis': '^5.6.1',
|
|
97
|
+
'jsonwebtoken': '^9.0.2',
|
|
98
|
+
'luxon': '^3.7.2',
|
|
99
|
+
'nodemailer': '^8.0.1',
|
|
100
|
+
'pg': '^8.16.3',
|
|
101
|
+
},
|
|
102
|
+
devDependencies: {
|
|
103
|
+
'@rapidd/build': '^2.1.3',
|
|
104
|
+
'@types/bcrypt': '^6.0.0',
|
|
105
|
+
'@types/ejs': '^3.1.5',
|
|
106
|
+
'@types/jsonwebtoken': '^9.0.8',
|
|
107
|
+
'@types/luxon': '^3.7.1',
|
|
108
|
+
'@types/node': '^22.12.0',
|
|
109
|
+
'@types/nodemailer': '^7.0.9',
|
|
110
|
+
'@types/pg': '^8.11.11',
|
|
111
|
+
'prisma': '^7.0.2',
|
|
112
|
+
'tsx': '^4.19.2',
|
|
113
|
+
'typescript': '^5.7.3',
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
fs.writeFileSync(
|
|
118
|
+
path.join(targetDir, 'package.json'),
|
|
119
|
+
JSON.stringify(pkg, null, 2) + '\n'
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
console.log('Project created. Next steps:\n');
|
|
123
|
+
console.log(' npm install');
|
|
124
|
+
console.log(' # Set DATABASE_URL in .env');
|
|
125
|
+
console.log(' npx prisma db pull');
|
|
126
|
+
console.log(' npx rapidd build');
|
|
127
|
+
console.log(' npm run dev\n');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function copyDir(src, dest, skip) {
|
|
131
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
132
|
+
|
|
133
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
134
|
+
if (skip.has(entry.name) || entry.name === '.DS_Store') continue;
|
|
135
|
+
|
|
136
|
+
const srcPath = path.join(src, entry.name);
|
|
137
|
+
const destPath = path.join(dest, entry.name);
|
|
138
|
+
|
|
139
|
+
if (entry.isDirectory()) {
|
|
140
|
+
copyDir(srcPath, destPath, skip);
|
|
141
|
+
} else {
|
|
142
|
+
fs.copyFileSync(srcPath, destPath);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|