jcc-express-mvc 1.9.5 → 1.9.7
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/final-documentation/Architecture Concept/Service-Provider.md +3 -1
- package/final-documentation/Database/Database-Introduction.md +6 -0
- package/final-documentation/Database/Prisma.md +275 -0
- package/final-documentation/Database/TypeORM.md +240 -0
- package/final-documentation/Digging Deeper/Helpers.md +6 -0
- package/final-documentation/Getting-Started/Configuration.md +1 -1
- package/final-documentation/Getting-Started/Directory-structure.md +1 -1
- package/final-documentation/Security/Authentication.md +2 -2
- package/final-documentation/The Basics/API-Resources.md +121 -0
- package/final-documentation/The Basics/Artisan-Node.md +15 -0
- package/final-documentation/The Basics/Response.md +1 -1
- package/global.d.ts +2 -0
- package/lib/Application/ApplicationBuilder.d.ts.map +1 -1
- package/lib/Application/ApplicationBuilder.js +4 -0
- package/lib/Auth/AuthMiddleware.d.ts.map +1 -1
- package/lib/Auth/AuthMiddleware.js +6 -0
- package/lib/Auth/index.js +3 -3
- package/lib/Auth/type.d.ts +1 -1
- package/lib/Command-Line/MakeCommand.d.ts +1 -0
- package/lib/Command-Line/MakeCommand.d.ts.map +1 -1
- package/lib/Command-Line/MakeCommand.js +65 -1
- package/lib/Command-Line/NodeArtisanCommand.d.ts +2 -0
- package/lib/Command-Line/NodeArtisanCommand.d.ts.map +1 -1
- package/lib/Command-Line/NodeArtisanCommand.js +19 -0
- package/lib/Command-Line/PrismaCommand.d.ts +9 -0
- package/lib/Command-Line/PrismaCommand.d.ts.map +1 -0
- package/lib/Command-Line/PrismaCommand.js +46 -0
- package/lib/Command-Line/files/Models.d.ts.map +1 -1
- package/lib/Command-Line/files/Models.js +16 -0
- package/lib/Command-Line/files/Resource.d.ts +8 -0
- package/lib/Command-Line/files/Resource.d.ts.map +1 -0
- package/lib/Command-Line/files/Resource.js +25 -0
- package/lib/Config/Config.d.ts +1 -1
- package/lib/Config/Config.d.ts.map +1 -1
- package/lib/Container/index.d.ts.map +1 -1
- package/lib/Database/Database.d.ts +1 -6
- package/lib/Database/Database.d.ts.map +1 -1
- package/lib/Database/Database.js +34 -9
- package/lib/Database/DatabaseServiceProvider.d.ts.map +1 -1
- package/lib/Database/DatabaseServiceProvider.js +5 -0
- package/lib/Database/Drivers/Prisma/adapter.d.ts +14 -0
- package/lib/Database/Drivers/Prisma/adapter.d.ts.map +1 -0
- package/lib/Database/Drivers/Prisma/adapter.js +50 -0
- package/lib/Database/Drivers/Prisma/adapters/mariadb.d.ts +6 -0
- package/lib/Database/Drivers/Prisma/adapters/mariadb.d.ts.map +1 -0
- package/lib/Database/Drivers/Prisma/adapters/mariadb.js +28 -0
- package/lib/Database/Drivers/Prisma/adapters/postgres.d.ts +4 -0
- package/lib/Database/Drivers/Prisma/adapters/postgres.d.ts.map +1 -0
- package/lib/Database/Drivers/Prisma/adapters/postgres.js +30 -0
- package/lib/Database/Drivers/Prisma/adapters/sqlite.d.ts +6 -0
- package/lib/Database/Drivers/Prisma/adapters/sqlite.d.ts.map +1 -0
- package/lib/Database/Drivers/Prisma/adapters/sqlite.js +60 -0
- package/lib/Database/Drivers/Prisma/connection.d.ts +5 -0
- package/lib/Database/Drivers/Prisma/connection.d.ts.map +1 -0
- package/lib/Database/Drivers/Prisma/connection.js +14 -0
- package/lib/Database/Drivers/Prisma/readConnectionEnv.d.ts +3 -0
- package/lib/Database/Drivers/Prisma/readConnectionEnv.d.ts.map +1 -0
- package/lib/Database/Drivers/Prisma/readConnectionEnv.js +15 -0
- package/lib/Database/Drivers/Prisma/register.d.ts +7 -0
- package/lib/Database/Drivers/Prisma/register.d.ts.map +1 -0
- package/lib/Database/Drivers/Prisma/register.js +47 -0
- package/lib/Database/Drivers/Prisma/types.d.ts +18 -0
- package/lib/Database/Drivers/Prisma/types.d.ts.map +1 -0
- package/lib/Database/Drivers/Prisma/types.js +2 -0
- package/lib/Database/Drivers/PrismaDriver.d.ts +24 -0
- package/lib/Database/Drivers/PrismaDriver.d.ts.map +1 -0
- package/lib/Database/Drivers/PrismaDriver.js +41 -0
- package/lib/Database/Drivers/TypeORM/connection.d.ts +6 -0
- package/lib/Database/Drivers/TypeORM/connection.d.ts.map +1 -0
- package/lib/Database/Drivers/TypeORM/connection.js +45 -0
- package/lib/Database/Drivers/TypeORM/register.d.ts +7 -0
- package/lib/Database/Drivers/TypeORM/register.d.ts.map +1 -0
- package/lib/Database/Drivers/TypeORM/register.js +47 -0
- package/lib/Database/Drivers/TypeORM/types.d.ts +19 -0
- package/lib/Database/Drivers/TypeORM/types.d.ts.map +1 -0
- package/lib/Database/Drivers/TypeORM/types.js +2 -0
- package/lib/Database/Drivers/TypeORMDriver.d.ts +30 -0
- package/lib/Database/Drivers/TypeORMDriver.d.ts.map +1 -0
- package/lib/Database/Drivers/TypeORMDriver.js +81 -0
- package/lib/Database/Prisma/connection.d.ts +6 -0
- package/lib/Database/Prisma/connection.d.ts.map +1 -0
- package/lib/Database/Prisma/connection.js +19 -0
- package/lib/Database/PrismaServiceProvider.d.ts +12 -0
- package/lib/Database/PrismaServiceProvider.d.ts.map +1 -0
- package/lib/Database/PrismaServiceProvider.js +45 -0
- package/lib/Database/TypeORMServiceProvider.d.ts +12 -0
- package/lib/Database/TypeORMServiceProvider.d.ts.map +1 -0
- package/lib/Database/TypeORMServiceProvider.js +45 -0
- package/lib/Database/index.d.ts +9 -0
- package/lib/Database/index.d.ts.map +1 -1
- package/lib/Database/index.js +22 -1
- package/lib/Database/interface.d.ts +1 -1
- package/lib/Database/isPrismaOrm.d.ts +3 -0
- package/lib/Database/isPrismaOrm.d.ts.map +1 -0
- package/lib/Database/isPrismaOrm.js +7 -0
- package/lib/Database/isTypeORMOrm.d.ts +3 -0
- package/lib/Database/isTypeORMOrm.d.ts.map +1 -0
- package/lib/Database/isTypeORMOrm.js +7 -0
- package/lib/Database/type.d.ts +1 -1
- package/lib/Database/type.d.ts.map +1 -1
- package/lib/Global/helpers.d.ts.map +1 -1
- package/lib/Global/helpers.js +6 -0
- package/lib/Interface/index.d.ts +6 -0
- package/lib/Interface/index.d.ts.map +1 -1
- package/lib/Middleware/index.js +1 -1
- package/lib/Monitor/MonitorServiceProvider.d.ts.map +1 -1
- package/lib/Monitor/MonitorServiceProvider.js +41 -31
- package/lib/Request/FormRequest.d.ts +6 -0
- package/lib/Request/FormRequest.d.ts.map +1 -1
- package/lib/Request/FormRequest.js +8 -0
- package/lib/Request/request.d.ts +6 -0
- package/lib/Request/request.d.ts.map +1 -1
- package/lib/Request/request.js +12 -1
- package/lib/Resources/JsonResources.d.ts +27 -0
- package/lib/Resources/JsonResources.d.ts.map +1 -0
- package/lib/Resources/JsonResources.js +137 -0
- package/lib/Resources/ResourceCollection.d.ts +15 -0
- package/lib/Resources/ResourceCollection.d.ts.map +1 -0
- package/lib/Resources/ResourceCollection.js +32 -0
- package/lib/Resources/helpers.d.ts +8 -0
- package/lib/Resources/helpers.d.ts.map +1 -0
- package/lib/Resources/helpers.js +38 -0
- package/lib/Resources/index.d.ts +5 -0
- package/lib/Resources/index.d.ts.map +1 -0
- package/lib/Resources/index.js +9 -0
- package/lib/Resources/types.d.ts +10 -0
- package/lib/Resources/types.d.ts.map +1 -0
- package/lib/Resources/types.js +5 -0
- package/lib/util/index.d.ts +2 -0
- package/lib/util/index.d.ts.map +1 -1
- package/lib/util/index.js +49 -2
- package/package.json +1 -1
- package/lib/Passport/config.d.ts +0 -14
- package/lib/Passport/config.d.ts.map +0 -1
- package/lib/Passport/config.js +0 -93
|
@@ -32,7 +32,8 @@ The abstract base class lives in `jcc-express-mvc` (`ServiceProvider`). Extend i
|
|
|
32
32
|
|
|
33
33
|
In `withProviders()`, the builder does not use your array alone. It builds this chain:
|
|
34
34
|
|
|
35
|
-
1. `DatabaseServiceProvider` —
|
|
35
|
+
1. `DatabaseServiceProvider` — Knex / Sequelize / Mongoose setup (skipped when `DB_ORM=prisma`).
|
|
36
|
+
2. `PrismaServiceProvider` — Prisma client singleton and graceful `$disconnect`.
|
|
36
37
|
2. Your providers from `bootstrap/providers.ts` — If `AuthServiceProvider` (framework) is in the list but not first, the builder moves it to the front of your list so auth is set up early.
|
|
37
38
|
3. `QueueServiceProvider` — Always last in the chain.
|
|
38
39
|
|
|
@@ -98,6 +99,7 @@ export const providers = [AppServiceProvider];
|
|
|
98
99
|
## Framework providers you should know about
|
|
99
100
|
|
|
100
101
|
- `DatabaseServiceProvider` — Database / ORM wiring; runs before your list.
|
|
102
|
+
- `PrismaServiceProvider` — Registers `database.prisma.service` when configured.
|
|
101
103
|
- `AuthServiceProvider` (when included) — Intended to run early among your providers.
|
|
102
104
|
- `QueueServiceProvider` — Queue binding; runs after your list.
|
|
103
105
|
- `RouteServiceProvider` — Not in your `bootstrap/providers.ts` list by default; it is registered as a singleton on `Application` and `loadRoutes()` is invoked from `app.run()` to load `route/web`, `route/api`, etc.
|
|
@@ -7,6 +7,7 @@ Almost every real application needs a database. JCC Express MVC keeps this flexi
|
|
|
7
7
|
- **JCC ORM (default)** for SQL with a Knex-backed query layer
|
|
8
8
|
- **Sequelize** as an alternative SQL ORM
|
|
9
9
|
- **Mongoose** for MongoDB document workflows
|
|
10
|
+
- **Prisma** for schema-first SQL with generated client and migrations
|
|
10
11
|
|
|
11
12
|
In practice, most projects start with the default JCC stack, then switch ORM mode only when needed.
|
|
12
13
|
|
|
@@ -23,6 +24,8 @@ With the default JCC SQL path (`DB_ORM=jcc`), connection clients include:
|
|
|
23
24
|
|
|
24
25
|
For document databases, use `DB_ORM=mongoose` with your Mongo connection settings.
|
|
25
26
|
|
|
27
|
+
For Prisma, use `DB_ORM=prisma` and configure `DATABASE_URL` plus `database.prisma.service` — see [Prisma](./Prisma.md).
|
|
28
|
+
|
|
26
29
|
---
|
|
27
30
|
|
|
28
31
|
## Configuration
|
|
@@ -38,11 +41,14 @@ Minimal shape from `app/Config/database.ts`:
|
|
|
38
41
|
```typescript
|
|
39
42
|
export const database = {
|
|
40
43
|
orm: config.get("DB_ORM", "jcc"),
|
|
44
|
+
prisma: { service: PrismaService },
|
|
41
45
|
sequelize: { /* ... */ },
|
|
42
46
|
mongoose: { /* ... */ },
|
|
43
47
|
};
|
|
44
48
|
```
|
|
45
49
|
|
|
50
|
+
See [Prisma](./Prisma.md) for the full Prisma setup.
|
|
51
|
+
|
|
46
52
|
---
|
|
47
53
|
|
|
48
54
|
## SQLite configuration
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# Prisma
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
JCC Express MVC supports [Prisma](https://www.prisma.io/) as an ORM option via `DB_ORM=prisma`. Prisma uses its own schema (`prisma/schema.prisma`), migrations, and generated client — separate from JCC Eloquent models in `app/Models/`.
|
|
6
|
+
|
|
7
|
+
You can also use Prisma **alongside** JCC Eloquent by keeping `DB_ORM=jcc` and configuring `database.prisma.service` in `app/Config/database.ts` (the framework still registers a singleton Prisma client).
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Enable Prisma
|
|
12
|
+
|
|
13
|
+
### Option A — Prisma as primary ORM
|
|
14
|
+
|
|
15
|
+
```env
|
|
16
|
+
DB_ORM=prisma
|
|
17
|
+
|
|
18
|
+
DATABASE_URL="mysql://root:password@127.0.0.1:3306/your_database"
|
|
19
|
+
DATABASE_HOST=127.0.0.1
|
|
20
|
+
DATABASE_PORT=3306
|
|
21
|
+
DATABASE_USER=root
|
|
22
|
+
DATABASE_PASSWORD=password
|
|
23
|
+
DATABASE_NAME=your_database
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`DATABASE_URL` is used by the Prisma CLI (`prisma.config.ts`). **The framework does not pick a default runtime adapter.** You must configure one explicitly (see below).
|
|
27
|
+
|
|
28
|
+
Built-in adapter factories (install the matching package when using `PRISMA_ADAPTER` or `database.prisma.adapter`):
|
|
29
|
+
|
|
30
|
+
| `PRISMA_ADAPTER` | Package |
|
|
31
|
+
| ---------------- | --------------------------------------------------- |
|
|
32
|
+
| `mariadb` | `@prisma/adapter-mariadb` |
|
|
33
|
+
| `postgres` | `@prisma/adapter-pg` + `pg` |
|
|
34
|
+
| `sqlite` | `@prisma/adapter-better-sqlite3` + `better-sqlite3` |
|
|
35
|
+
| `libsql` | `@prisma/adapter-libsql` |
|
|
36
|
+
|
|
37
|
+
```env
|
|
38
|
+
PRISMA_ADAPTER=mariadb
|
|
39
|
+
DATABASE_URL="mysql://root:password@127.0.0.1:3306/your_database"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or configure the adapter in `PrismaService` / `database.prisma.adapter` instead of `PRISMA_ADAPTER`.
|
|
43
|
+
|
|
44
|
+
### Option B — Prisma alongside JCC Eloquent
|
|
45
|
+
|
|
46
|
+
Keep `DB_ORM=jcc` for Knex/JCC models and ensure `app/Config/database.ts` includes:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { PrismaService } from "@/Services/PrismaService";
|
|
50
|
+
|
|
51
|
+
export const database = {
|
|
52
|
+
orm: config.get("DB_ORM", "jcc"),
|
|
53
|
+
prisma: {
|
|
54
|
+
service: PrismaService,
|
|
55
|
+
},
|
|
56
|
+
// ...
|
|
57
|
+
};
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`PrismaServiceProvider` registers the client whenever `database.prisma.service` is set.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Required packages
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npm install @prisma/client @prisma/adapter-mariadb
|
|
68
|
+
npm install -D prisma
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Initialize Prisma (new projects)
|
|
74
|
+
|
|
75
|
+
After installing the packages, scaffold Prisma in your app **once** from the project root. Skip this step if `prisma/schema.prisma` already exists.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
bun --bun x prisma init --datasource-provider mysql --output ./generated/prisma
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Equivalent with `npx`:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npx prisma init --datasource-provider mysql --output ./generated/prisma
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
This creates:
|
|
88
|
+
|
|
89
|
+
| File | Purpose |
|
|
90
|
+
| ---------------------- | -------------------------------------------- |
|
|
91
|
+
| `prisma/schema.prisma` | Your Prisma schema |
|
|
92
|
+
| `prisma.config.ts` | CLI config (`DATABASE_URL`, migrations path) |
|
|
93
|
+
|
|
94
|
+
Use `./generated/prisma` as the client output — **not** `../src/generated/prisma`. JCC Express MVC imports the client from `generated/prisma/client` (see `PrismaService` below).
|
|
95
|
+
|
|
96
|
+
Set your database URL in `.env` before generating or migrating:
|
|
97
|
+
|
|
98
|
+
```env
|
|
99
|
+
DATABASE_URL="mysql://root:password@127.0.0.1:3306/your_database"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Then add `app/Services/PrismaService.ts` and wire `database.prisma.service` in `app/Config/database.ts` (see [App service class](#app-service-class)).
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Generate the client
|
|
107
|
+
|
|
108
|
+
Generate the client after `prisma init` and whenever the schema changes:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
bun artisanNode prisma:generate
|
|
112
|
+
# or
|
|
113
|
+
npm run prisma:generate
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The generated client is written to `generated/prisma/` (gitignored — run `prisma:generate` after clone/CI install).
|
|
117
|
+
|
|
118
|
+
Apply your first migration after generate:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
bun artisanNode prisma:migrate init
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## App service class
|
|
127
|
+
|
|
128
|
+
The framework only injects an adapter when you set `database.prisma.adapter` or `PRISMA_ADAPTER`. Otherwise `PrismaService` owns the adapter:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { PrismaClient } from "generated/prisma/client";
|
|
132
|
+
import { createMariaDbAdapter } from "@framework/lib/Database/Drivers/Prisma/adapters/mariadb";
|
|
133
|
+
import type { PrismaClientOptions } from "@framework/lib/Database/Drivers/Prisma/types";
|
|
134
|
+
|
|
135
|
+
export class PrismaService extends PrismaClient {
|
|
136
|
+
constructor(options?: PrismaClientOptions) {
|
|
137
|
+
super({
|
|
138
|
+
adapter: options?.adapter ?? createMariaDbAdapter(), // your choice in the app
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
When the framework **does** inject an adapter (`options.adapter`), it takes precedence over your fallback.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Configuring an adapter
|
|
149
|
+
|
|
150
|
+
Choose one approach:
|
|
151
|
+
|
|
152
|
+
**1. In `PrismaService`** (app-owned — recommended when you want full control)
|
|
153
|
+
|
|
154
|
+
**2. `PRISMA_ADAPTER` env** — uses a built-in factory:
|
|
155
|
+
|
|
156
|
+
```env
|
|
157
|
+
PRISMA_ADAPTER=postgres
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**3. `database.prisma.adapter` in app config:**
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { createPostgresAdapter } from "jcc-express-mvc/lib/Database/Drivers/Prisma/adapters/postgres";
|
|
164
|
+
|
|
165
|
+
export const database = {
|
|
166
|
+
prisma: {
|
|
167
|
+
service: PrismaService,
|
|
168
|
+
adapter: () => createPostgresAdapter(),
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Fully custom** — pass any Prisma 7 adapter instance or factory:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { PrismaPg } from "@prisma/adapter-pg";
|
|
177
|
+
import { Pool } from "pg";
|
|
178
|
+
|
|
179
|
+
prisma: {
|
|
180
|
+
service: PrismaService,
|
|
181
|
+
adapter: () => new PrismaPg(new Pool({ connectionString: process.env.DATABASE_URL })),
|
|
182
|
+
},
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`database.prisma.adapter` takes precedence over `PRISMA_ADAPTER`. If neither is set, the framework passes no adapter and `PrismaService` decides.
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Framework adapter helpers
|
|
190
|
+
|
|
191
|
+
| Export | Use |
|
|
192
|
+
| --------------------------- | --------------------------------------------------- |
|
|
193
|
+
| `resolvePrismaAdapter(app)` | Used by `PrismaServiceProvider` |
|
|
194
|
+
| `createPrismaAdapter(name)` | Build adapter for a named driver (`PRISMA_ADAPTER`) |
|
|
195
|
+
| `createMariaDbAdapter()` | MySQL / MariaDB |
|
|
196
|
+
| `createPostgresAdapter()` | PostgreSQL |
|
|
197
|
+
| `createSqliteAdapter()` | SQLite file |
|
|
198
|
+
| `createLibSqlAdapter()` | LibSQL / Turso |
|
|
199
|
+
|
|
200
|
+
Import from `jcc-express-mvc/lib/Database` or `jcc-express-mvc/lib/Database/Drivers/Prisma/connection`.
|
|
201
|
+
|
|
202
|
+
The framework registers `PrismaService` as a **singleton** and aliases `prisma` and `database.connection`.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Using Prisma in controllers
|
|
207
|
+
|
|
208
|
+
**Constructor injection** (recommended):
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import { Inject } from "jcc-express-mvc/Core/Dependency";
|
|
212
|
+
import { PrismaService } from "@/Services/PrismaService";
|
|
213
|
+
|
|
214
|
+
@Inject()
|
|
215
|
+
export class UsersController {
|
|
216
|
+
constructor(private readonly prisma: PrismaService) {}
|
|
217
|
+
|
|
218
|
+
async index() {
|
|
219
|
+
return await this.prisma.user.findMany();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**Global helper**:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
const users = await prisma().user.findMany();
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Schema and migrations
|
|
233
|
+
|
|
234
|
+
Schema: `prisma/schema.prisma`
|
|
235
|
+
Migrations: `prisma/migrations/`
|
|
236
|
+
CLI config: `prisma.config.ts`
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# Create & apply a migration (development)
|
|
240
|
+
bun artisanNode prisma:migrate init
|
|
241
|
+
|
|
242
|
+
# Apply pending migrations (production)
|
|
243
|
+
bun artisanNode prisma:deploy
|
|
244
|
+
|
|
245
|
+
# Push schema without migration files (prototyping)
|
|
246
|
+
bun artisanNode prisma:push
|
|
247
|
+
|
|
248
|
+
# Open Prisma Studio
|
|
249
|
+
bun artisanNode prisma:studio
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Equivalent npm scripts: `prisma:generate`, `prisma:migrate`, `prisma:studio`.
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Framework wiring
|
|
257
|
+
|
|
258
|
+
| Piece | Location |
|
|
259
|
+
| ----------------------- | ------------------------------------------------------------ |
|
|
260
|
+
| `PrismaDriver` | `jcc-express-mvc/lib/Database/Drivers/PrismaDriver.ts` |
|
|
261
|
+
| Driver adapters | `jcc-express-mvc/lib/Database/Drivers/Prisma/adapters/` |
|
|
262
|
+
| `PrismaServiceProvider` | `jcc-express-mvc/lib/Database/PrismaServiceProvider.ts` |
|
|
263
|
+
| `Database` resolver | `jcc-express-mvc/lib/Database/Database.ts` (`DB_ORM=prisma`) |
|
|
264
|
+
|
|
265
|
+
`PrismaDriver` implements the same `DatabaseDriver` interface as `KnexDriver`, `SequelizeDriver`, and `MongooseDriver` (`connect`, `getConnection`, `disconnect`). Adapter selection lives under `Drivers/Prisma/`.
|
|
266
|
+
|
|
267
|
+
When `DB_ORM=prisma`, `DatabaseServiceProvider` skips Knex/Sequelize/Mongoose setup so only Prisma owns the connection.
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Related
|
|
272
|
+
|
|
273
|
+
- [Database Introduction](./Database-Introduction.md)
|
|
274
|
+
- [ArtisanNode](../The%20Basics/Artisan-Node.md)
|
|
275
|
+
- [Service Provider](../Architecture%20Concept/Service-Provider.md)
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# TypeORM
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
JCC Express MVC supports [TypeORM](https://typeorm.io/) as an ORM option via `DB_ORM=typeorm`. TypeORM uses decorator-based **entities** in `app/Entities/` — separate from JCC Eloquent models in `app/Model/` and Sequelize/Mongoose files in `app/Models/`.
|
|
6
|
+
|
|
7
|
+
You can also use TypeORM **alongside** JCC Eloquent by keeping `DB_ORM=jcc` and configuring `database.typeorm.dataSource` in `app/Config/database.ts` (the framework still registers a singleton `DataSource`).
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Enable TypeORM
|
|
12
|
+
|
|
13
|
+
### Option A — TypeORM as primary ORM
|
|
14
|
+
|
|
15
|
+
```env
|
|
16
|
+
DB_ORM=typeorm
|
|
17
|
+
DB_CONNECTION=mysql2
|
|
18
|
+
DB_HOST=127.0.0.1
|
|
19
|
+
DB_PORT=3306
|
|
20
|
+
DB_DATABASE=your_database
|
|
21
|
+
DB_USERNAME=root
|
|
22
|
+
DB_PASSWORD=password
|
|
23
|
+
|
|
24
|
+
# Development only — use migrations in production
|
|
25
|
+
TYPEORM_SYNCHRONIZE=false
|
|
26
|
+
TYPEORM_LOGGING=false
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`DB_CONNECTION` is mapped to a TypeORM driver:
|
|
30
|
+
|
|
31
|
+
| `DB_CONNECTION` | TypeORM type |
|
|
32
|
+
| ------------------------------ | ---------------- |
|
|
33
|
+
| `mysql2`, `mysql`, `mariadb` | `mysql` |
|
|
34
|
+
| `postgres`, `postgresql`, `pg` | `postgres` |
|
|
35
|
+
| `sqlite`, `better-sqlite3` | `better-sqlite3` |
|
|
36
|
+
|
|
37
|
+
### Option B — TypeORM alongside JCC Eloquent
|
|
38
|
+
|
|
39
|
+
Keep `DB_ORM=jcc` for Knex/JCC models and ensure `app/Config/database.ts` includes:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { TypeORMDataSource } from "@/Services/TypeORMDataSource";
|
|
43
|
+
|
|
44
|
+
export const database = {
|
|
45
|
+
orm: config.get("DB_ORM", "jcc"),
|
|
46
|
+
typeorm: {
|
|
47
|
+
dataSource: TypeORMDataSource,
|
|
48
|
+
autoloadEntities: true,
|
|
49
|
+
synchronize: false,
|
|
50
|
+
logging: false,
|
|
51
|
+
},
|
|
52
|
+
// ...
|
|
53
|
+
};
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`TypeORMServiceProvider` registers the client whenever `database.typeorm.dataSource` is set.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Required packages
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npm install typeorm
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Install the driver for your database (already common in JCC apps):
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm install mysql2 # MySQL / MariaDB
|
|
70
|
+
npm install pg # PostgreSQL
|
|
71
|
+
npm install better-sqlite3 # SQLite
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## App DataSource class
|
|
77
|
+
|
|
78
|
+
`app/Services/TypeORMDataSource.ts` extends TypeORM `DataSource` and reads connection settings from `DB_*` env vars:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { DataSource } from "typeorm";
|
|
82
|
+
import { typeORMOptionsFromEnv } from "jcc-express-mvc/lib/Database/Drivers/TypeORM/connection";
|
|
83
|
+
import type { TypeORMDataSourceOptions } from "jcc-express-mvc/lib/Database/Drivers/TypeORM/types";
|
|
84
|
+
|
|
85
|
+
export class TypeORMDataSource extends DataSource {
|
|
86
|
+
constructor(options?: TypeORMDataSourceOptions) {
|
|
87
|
+
super(typeORMOptionsFromEnv(options));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Wire it in `app/Config/database.ts`:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
typeorm: {
|
|
96
|
+
dataSource: TypeORMDataSource,
|
|
97
|
+
autoloadEntities: true,
|
|
98
|
+
synchronize: config.get("TYPEORM_SYNCHRONIZE", "false") === "true",
|
|
99
|
+
logging: config.get("TYPEORM_LOGGING", "false") === "true",
|
|
100
|
+
},
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
When the driver connects, it scans `app/Entities/` for `.ts`/`.js` files and registers exported entity classes (unless `autoloadEntities: false` or you pass `entities` explicitly).
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Define entities
|
|
108
|
+
|
|
109
|
+
Place entities in `app/Entities/`:
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm";
|
|
113
|
+
import { Post } from "./Post";
|
|
114
|
+
|
|
115
|
+
@Entity({ name: "users" })
|
|
116
|
+
export class User {
|
|
117
|
+
@PrimaryGeneratedColumn()
|
|
118
|
+
id!: number;
|
|
119
|
+
|
|
120
|
+
@Column({ unique: true })
|
|
121
|
+
email!: string;
|
|
122
|
+
|
|
123
|
+
@Column({ nullable: true })
|
|
124
|
+
name?: string;
|
|
125
|
+
|
|
126
|
+
@Column({ select: false })
|
|
127
|
+
password!: string;
|
|
128
|
+
|
|
129
|
+
@OneToMany(() => Post, (post) => post.author)
|
|
130
|
+
posts!: Post[];
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Generate a new entity with ArtisanNode:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
bun artisanNode make:model Product
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
When `DB_ORM=typeorm`, `make:model` scaffolds into `app/Entities/` with TypeORM decorators.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Using TypeORM in controllers
|
|
145
|
+
|
|
146
|
+
**Constructor injection** (recommended):
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import { Inject } from "jcc-express-mvc/Core/Dependency";
|
|
150
|
+
import { TypeORMDataSource } from "@/Services/TypeORMDataSource";
|
|
151
|
+
import { User } from "@/Entities/User";
|
|
152
|
+
|
|
153
|
+
@Inject()
|
|
154
|
+
export class UsersController {
|
|
155
|
+
constructor(private readonly db: TypeORMDataSource) {}
|
|
156
|
+
|
|
157
|
+
async index() {
|
|
158
|
+
return this.db.getRepository(User).find();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async show(id: number) {
|
|
162
|
+
return this.db.getRepository(User).findOne({
|
|
163
|
+
where: { id },
|
|
164
|
+
relations: { posts: true },
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**Global helper**:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { User } from "@/Entities/User";
|
|
174
|
+
|
|
175
|
+
const users = await typeorm().getRepository(User).find();
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Auth and route model binding
|
|
181
|
+
|
|
182
|
+
When `DB_ORM=typeorm`, framework helpers resolve users and route-bound models via TypeORM repositories:
|
|
183
|
+
|
|
184
|
+
- `findUserById`, `findUserForAuth`
|
|
185
|
+
- `resolveModelBinding`
|
|
186
|
+
- `AuthMiddleware` API auth lookup
|
|
187
|
+
|
|
188
|
+
`getModel("User")` loads from `app/Entities/User` instead of `app/Models/`.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Migrations (TypeORM CLI)
|
|
193
|
+
|
|
194
|
+
Use the TypeORM CLI directly for migrations (not yet wrapped in ArtisanNode):
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
# Generate a migration from entity changes
|
|
198
|
+
npx typeorm migration:generate -d app/Services/TypeORMDataSource.ts migrations/Init
|
|
199
|
+
|
|
200
|
+
# Run migrations
|
|
201
|
+
npx typeorm migration:run -d app/Services/TypeORMDataSource.ts
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
For local prototyping only, `TYPEORM_SYNCHRONIZE=true` auto-syncs schema from entities. **Do not use synchronize in production.**
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Framework wiring
|
|
209
|
+
|
|
210
|
+
| Piece | Location |
|
|
211
|
+
| ------------------------ | ------------------------------------------------------------- |
|
|
212
|
+
| `TypeORMDriver` | `jcc-express-mvc/lib/Database/Drivers/TypeORMDriver.ts` |
|
|
213
|
+
| `TypeORMServiceProvider` | `jcc-express-mvc/lib/Database/TypeORMServiceProvider.ts` |
|
|
214
|
+
| Connection helpers | `jcc-express-mvc/lib/Database/Drivers/TypeORM/connection.ts` |
|
|
215
|
+
| `Database` resolver | `jcc-express-mvc/lib/Database/Database.ts` (`DB_ORM=typeorm`) |
|
|
216
|
+
|
|
217
|
+
`TypeORMDriver` implements the same `DatabaseDriver` interface as other drivers (`connect`, `getConnection`, `disconnect`).
|
|
218
|
+
|
|
219
|
+
When `DB_ORM=typeorm`, `DatabaseServiceProvider` skips Knex/Sequelize/Mongoose setup so only TypeORM owns the connection. Prisma and TypeORM do not register together when one is the primary `DB_ORM`.
|
|
220
|
+
|
|
221
|
+
The database **monitor** query collector is Knex-only; it is skipped automatically when `DB_ORM` is not `jcc`.
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Demo routes
|
|
226
|
+
|
|
227
|
+
This repo includes `TestTypeORMController` with routes under `/api/typeorm` when TypeORM is configured:
|
|
228
|
+
|
|
229
|
+
- `GET /api/typeorm` — list users with posts
|
|
230
|
+
- `POST /api/typeorm` — create sample user and post
|
|
231
|
+
- `GET /api/typeorm/:id` — show one user
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Related
|
|
236
|
+
|
|
237
|
+
- [Database Introduction](./Database-Introduction.md)
|
|
238
|
+
- [Configuration](../Getting-Started/Configuration.md)
|
|
239
|
+
- [Helpers](../Digging%20Deeper/Helpers.md)
|
|
240
|
+
- [Service Provider](../Architecture%20Concept/Service-Provider.md)
|
|
@@ -41,6 +41,12 @@ These depend on per-request container bindings, so they are request-lifecycle he
|
|
|
41
41
|
- `can(user, ability, model?, ...args)`
|
|
42
42
|
- `authorize(user, ability, ...args)`
|
|
43
43
|
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Database helpers
|
|
47
|
+
|
|
48
|
+
- `prisma()` -> registered Prisma client singleton (when `database.prisma.service` is configured or `DB_ORM=prisma`)
|
|
49
|
+
|
|
44
50
|
Also available:
|
|
45
51
|
|
|
46
52
|
- `bcrypt(value)`
|
|
@@ -33,7 +33,7 @@ Use a full URL for `APP_URL` in production (for example `https://example.com`).
|
|
|
33
33
|
|
|
34
34
|
### Database and ORM
|
|
35
35
|
|
|
36
|
-
- `DB_ORM` — `jcc` (Knex / JCC Eloquent), `sequelize`, or `
|
|
36
|
+
- `DB_ORM` — `jcc` (Knex / JCC Eloquent), `sequelize`, `mongoose`, or `prisma`.
|
|
37
37
|
- `DB_CONNECTION` — Driver for Knex (for example `mysql2`, `postgres`, `sqlite`, `better-sqlite3`).
|
|
38
38
|
- `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD` — Connection details.
|
|
39
39
|
|
|
@@ -35,7 +35,7 @@ Other roots you may see in a mature repo include `design/` (schemas, design note
|
|
|
35
35
|
## `app/` — application code
|
|
36
36
|
|
|
37
37
|
- `Config/` — Typed config modules merged in `app/Config/index.ts` (see [Configuration.md](./Configuration.md)).
|
|
38
|
-
- `Http/` — `kernel.ts`, `Controllers/`, `ApiControllers/`, `Middlewares/`, `Requests/`.
|
|
38
|
+
- `Http/` — `kernel.ts`, `Controllers/`, `ApiControllers/`, `Middlewares/`, `Requests/`, `Resources/`.
|
|
39
39
|
- `Models/` — Eloquent (or other ORM) models.
|
|
40
40
|
- `Providers/` — Service providers (`AppServiceProvider`, queues, sockets, etc.).
|
|
41
41
|
- `Console/Command/` — Custom `artisanNode` commands.
|
|
@@ -107,7 +107,7 @@ For stateless API clients, use `auth().apiAttempt()` to validate credentials and
|
|
|
107
107
|
|
|
108
108
|
```typescript
|
|
109
109
|
Route.post("/api/login", async () => {
|
|
110
|
-
const result = await auth().apiAttempt({ field: "email",
|
|
110
|
+
const result = await auth().apiAttempt({ field: "email", model: "User" });
|
|
111
111
|
|
|
112
112
|
if (!result.success) {
|
|
113
113
|
return response().status(401).json({ message: result.message });
|
|
@@ -129,7 +129,7 @@ Route.middleware(["apiAuth"]).get("/api/me", async (req) => {
|
|
|
129
129
|
| Field | Default | Description |
|
|
130
130
|
|---|---|---|
|
|
131
131
|
| `field` | `"email"` | Request input key to match (`email`, `phone`, `username`, etc.) |
|
|
132
|
-
| `
|
|
132
|
+
| `model` | `"User"` | Model name resolved via `getModel()` |
|
|
133
133
|
|
|
134
134
|
The request body must include the credential field and `password`. On success, returns `{ success: true, token, user, message }`. On failure, returns `{ success: false, token: null, user: null, message: "Invalid credentials" }`.
|
|
135
135
|
|