exisjs 0.5.9 → 0.5.11
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/dist/auth/index.d.ts +3 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +4 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/oauth/core.d.ts +96 -0
- package/dist/auth/oauth/core.d.ts.map +1 -0
- package/dist/auth/oauth/core.js +203 -0
- package/dist/auth/oauth/core.js.map +1 -0
- package/dist/auth/oauth/index.d.ts +45 -0
- package/dist/auth/oauth/index.d.ts.map +1 -0
- package/dist/auth/oauth/index.js +64 -0
- package/dist/auth/oauth/index.js.map +1 -0
- package/dist/auth/oauth/middleware.d.ts +63 -0
- package/dist/auth/oauth/middleware.d.ts.map +1 -0
- package/dist/auth/oauth/middleware.js +164 -0
- package/dist/auth/oauth/middleware.js.map +1 -0
- package/dist/auth/oauth/providers/custom.d.ts +41 -0
- package/dist/auth/oauth/providers/custom.d.ts.map +1 -0
- package/dist/auth/oauth/providers/custom.js +204 -0
- package/dist/auth/oauth/providers/custom.js.map +1 -0
- package/dist/auth/oauth/providers/discord.d.ts +23 -0
- package/dist/auth/oauth/providers/discord.d.ts.map +1 -0
- package/dist/auth/oauth/providers/discord.js +130 -0
- package/dist/auth/oauth/providers/discord.js.map +1 -0
- package/dist/auth/oauth/providers/facebook.d.ts +28 -0
- package/dist/auth/oauth/providers/facebook.d.ts.map +1 -0
- package/dist/auth/oauth/providers/facebook.js +124 -0
- package/dist/auth/oauth/providers/facebook.js.map +1 -0
- package/dist/auth/oauth/providers/github.d.ts +25 -0
- package/dist/auth/oauth/providers/github.d.ts.map +1 -0
- package/dist/auth/oauth/providers/github.js +132 -0
- package/dist/auth/oauth/providers/github.js.map +1 -0
- package/dist/auth/oauth/providers/google.d.ts +23 -0
- package/dist/auth/oauth/providers/google.d.ts.map +1 -0
- package/dist/auth/oauth/providers/google.js +134 -0
- package/dist/auth/oauth/providers/google.js.map +1 -0
- package/dist/auth/oauth/providers/microsoft.d.ts +28 -0
- package/dist/auth/oauth/providers/microsoft.d.ts.map +1 -0
- package/dist/auth/oauth/providers/microsoft.js +136 -0
- package/dist/auth/oauth/providers/microsoft.js.map +1 -0
- package/dist/auth/oauth/types.d.ts +158 -0
- package/dist/auth/oauth/types.d.ts.map +1 -0
- package/dist/auth/oauth/types.js +4 -0
- package/dist/auth/oauth/types.js.map +1 -0
- package/dist/auth/session.d.ts +4 -1
- package/dist/auth/session.d.ts.map +1 -1
- package/dist/auth/session.js +33 -5
- package/dist/auth/session.js.map +1 -1
- package/dist/cache/store.d.ts +11 -3
- package/dist/cache/store.d.ts.map +1 -1
- package/dist/cache/store.js +87 -19
- package/dist/cache/store.js.map +1 -1
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +4 -6
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/database/adapters/mongodb.d.ts +58 -0
- package/dist/database/adapters/mongodb.d.ts.map +1 -0
- package/dist/database/adapters/mongodb.js +264 -0
- package/dist/database/adapters/mongodb.js.map +1 -0
- package/dist/database/adapters/mysql.d.ts +27 -0
- package/dist/database/adapters/mysql.d.ts.map +1 -0
- package/dist/database/adapters/mysql.js +188 -0
- package/dist/database/adapters/mysql.js.map +1 -0
- package/dist/database/adapters/postgres.d.ts +28 -0
- package/dist/database/adapters/postgres.d.ts.map +1 -0
- package/dist/database/adapters/postgres.js +174 -0
- package/dist/database/adapters/postgres.js.map +1 -0
- package/dist/database/adapters/sqlite.d.ts +27 -0
- package/dist/database/adapters/sqlite.d.ts.map +1 -0
- package/dist/database/adapters/sqlite.js +149 -0
- package/dist/database/adapters/sqlite.js.map +1 -0
- package/dist/database/index.d.ts +42 -0
- package/dist/database/index.d.ts.map +1 -0
- package/dist/database/index.js +58 -0
- package/dist/database/index.js.map +1 -0
- package/dist/database/manager.d.ts +114 -0
- package/dist/database/manager.d.ts.map +1 -0
- package/dist/database/manager.js +269 -0
- package/dist/database/manager.js.map +1 -0
- package/dist/database/migration.d.ts +57 -0
- package/dist/database/migration.d.ts.map +1 -0
- package/dist/database/migration.js +329 -0
- package/dist/database/migration.js.map +1 -0
- package/dist/database/query-builder.d.ts +89 -0
- package/dist/database/query-builder.d.ts.map +1 -0
- package/dist/database/query-builder.js +353 -0
- package/dist/database/query-builder.js.map +1 -0
- package/dist/database/types.d.ts +184 -0
- package/dist/database/types.d.ts.map +1 -0
- package/dist/database/types.js +4 -0
- package/dist/database/types.js.map +1 -0
- package/dist/docs/ARCHITECTURE.md +349 -0
- package/dist/docs/BENCHMARKS.md +36 -0
- package/dist/docs/README.md +34 -0
- package/dist/docs/content/README.md +44 -0
- package/dist/docs/content/adapters.mdx +199 -0
- package/dist/docs/content/cache.mdx +239 -0
- package/dist/docs/content/circuit-breaker.mdx +130 -0
- package/dist/docs/content/cli/commands.mdx +214 -0
- package/dist/docs/content/cli/overview.mdx +92 -0
- package/dist/docs/content/cli/usage.mdx +61 -0
- package/dist/docs/content/config.mdx +214 -0
- package/dist/docs/content/controllers.mdx +241 -0
- package/dist/docs/content/cron.mdx +109 -0
- package/dist/docs/content/database.mdx +209 -0
- package/dist/docs/content/dataloader.mdx +170 -0
- package/dist/docs/content/decorators.mdx +204 -0
- package/dist/docs/content/errors.mdx +344 -0
- package/dist/docs/content/gateways.mdx +376 -0
- package/dist/docs/content/installation.mdx +210 -0
- package/dist/docs/content/introduction.mdx +96 -0
- package/dist/docs/content/middleware.mdx +441 -0
- package/dist/docs/content/modules.mdx +144 -0
- package/dist/docs/content/oauth.mdx +179 -0
- package/dist/docs/content/providers.mdx +147 -0
- package/dist/docs/content/queues.mdx +177 -0
- package/dist/docs/content/requests.mdx +293 -0
- package/dist/docs/content/responses.mdx +271 -0
- package/dist/docs/content/routing.mdx +541 -0
- package/dist/docs/content/rpc.mdx +146 -0
- package/dist/docs/content/security.mdx +294 -0
- package/dist/docs/content/sse.mdx +159 -0
- package/dist/docs/content/structure.mdx +116 -0
- package/dist/docs/content/testing.mdx +139 -0
- package/dist/docs/content/validation.mdx +249 -0
- package/dist/docs/content/websockets.mdx +289 -0
- package/dist/docs/integrations/README.md +23 -0
- package/dist/docs/integrations/jwt.mdx +151 -0
- package/dist/docs/integrations/mongo.mdx +183 -0
- package/dist/docs/integrations/observability.mdx +125 -0
- package/dist/docs/integrations/openai.mdx +108 -0
- package/dist/docs/integrations/postgres.mdx +190 -0
- package/dist/docs/integrations/posthog.mdx +122 -0
- package/dist/docs/integrations/redis.mdx +141 -0
- package/dist/docs/integrations/resend.mdx +117 -0
- package/dist/docs/integrations/s3.mdx +122 -0
- package/dist/docs/integrations/swagger.mdx +113 -0
- package/dist/docs/reference/README.md +3 -0
- package/dist/docs/reference/adapters.mdx +130 -0
- package/dist/docs/reference/auth.mdx +183 -0
- package/dist/docs/reference/cache.mdx +132 -0
- package/dist/docs/reference/circuit-breaker.mdx +71 -0
- package/dist/docs/reference/config.mdx +118 -0
- package/dist/docs/reference/context.mdx +168 -0
- package/dist/docs/reference/dataloader.mdx +91 -0
- package/dist/docs/reference/decorators.mdx +293 -0
- package/dist/docs/reference/di.mdx +123 -0
- package/dist/docs/reference/error.mdx +170 -0
- package/dist/docs/reference/exis.mdx +124 -0
- package/dist/docs/reference/middleware.mdx +304 -0
- package/dist/docs/reference/plugin.mdx +113 -0
- package/dist/docs/reference/queue.mdx +155 -0
- package/dist/docs/reference/router.mdx +248 -0
- package/dist/docs/reference/sanitize.mdx +130 -0
- package/dist/docs/reference/storage.mdx +116 -0
- package/dist/docs/reference/swagger.mdx +132 -0
- package/dist/docs/reference/testing.mdx +145 -0
- package/dist/docs/reference/validator.mdx +186 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware/backpressure.d.ts.map +1 -1
- package/dist/middleware/backpressure.js +21 -12
- package/dist/middleware/backpressure.js.map +1 -1
- package/dist/middleware/cache.d.ts +6 -0
- package/dist/middleware/cache.d.ts.map +1 -1
- package/dist/middleware/cache.js +84 -28
- package/dist/middleware/cache.js.map +1 -1
- package/dist/middleware/idempotency.d.ts +4 -1
- package/dist/middleware/idempotency.d.ts.map +1 -1
- package/dist/middleware/idempotency.js +29 -4
- package/dist/middleware/idempotency.js.map +1 -1
- package/dist/middleware/ip-filter.d.ts.map +1 -1
- package/dist/middleware/ip-filter.js +16 -0
- package/dist/middleware/ip-filter.js.map +1 -1
- package/dist/middleware/rate-limit.d.ts.map +1 -1
- package/dist/middleware/rate-limit.js +29 -12
- package/dist/middleware/rate-limit.js.map +1 -1
- package/dist/queue/drivers/MemoryDriver.d.ts +6 -3
- package/dist/queue/drivers/MemoryDriver.d.ts.map +1 -1
- package/dist/queue/drivers/MemoryDriver.js +76 -23
- package/dist/queue/drivers/MemoryDriver.js.map +1 -1
- package/dist/server/bootstrapper.d.ts +3 -0
- package/dist/server/bootstrapper.d.ts.map +1 -1
- package/dist/server/bootstrapper.js +56 -8
- package/dist/server/bootstrapper.js.map +1 -1
- package/dist/server/bun-adapter.d.ts +2 -0
- package/dist/server/bun-adapter.d.ts.map +1 -1
- package/dist/server/bun-adapter.js +23 -12
- package/dist/server/bun-adapter.js.map +1 -1
- package/dist/server/request-handler.d.ts +0 -2
- package/dist/server/request-handler.d.ts.map +1 -1
- package/dist/server/request-handler.js +2 -24
- package/dist/server/request-handler.js.map +1 -1
- package/dist/server/request.d.ts.map +1 -1
- package/dist/server/request.js +24 -11
- package/dist/server/request.js.map +1 -1
- package/dist/server/uws-adapter.d.ts +80 -0
- package/dist/server/uws-adapter.d.ts.map +1 -0
- package/dist/server/uws-adapter.js +357 -0
- package/dist/server/uws-adapter.js.map +1 -0
- package/dist/server/ws-orchestrator.d.ts +1 -0
- package/dist/server/ws-orchestrator.d.ts.map +1 -1
- package/dist/server/ws-orchestrator.js +83 -0
- package/dist/server/ws-orchestrator.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/config.js.map +1 -1
- package/dist/validator/tex.d.ts +21 -16
- package/dist/validator/tex.d.ts.map +1 -1
- package/dist/validator/tex.js.map +1 -1
- package/package.json +11 -3
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
---
|
|
2
|
+
nextUrl: "/docs/integrations/swagger"
|
|
3
|
+
nextTitle: "Swagger OpenAPI"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# JWT Authentication
|
|
7
|
+
|
|
8
|
+
ExisJS comes with a native JSON Web Token (JWT) integration. You do **not** need to install external dependencies like `jsonwebtoken`. It is built natively into the framework and optimized for edge environments.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Environment Variables
|
|
13
|
+
|
|
14
|
+
The JWT integration securely reads your secret key from your `.env` file.
|
|
15
|
+
Before using the plugin, ensure you have your JWT secret set:
|
|
16
|
+
|
|
17
|
+
```bash filename=".env"
|
|
18
|
+
# Your JWT signing secret
|
|
19
|
+
JWT_SECRET="super_secret_key_123"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
Import the `JWT` object from `exisjs/auth` and use `JWT.signJWT()` or `JWT.verifyJWT()`. You will need to explicitly pass your secret key.
|
|
27
|
+
|
|
28
|
+
#### Functional Paradigm
|
|
29
|
+
|
|
30
|
+
```typescript filename="src/http/auth/route.ts"
|
|
31
|
+
import { controller, route } from 'exisjs/router'
|
|
32
|
+
import { JWT } from 'exisjs/auth'
|
|
33
|
+
import { db, users } from '../../db'
|
|
34
|
+
|
|
35
|
+
export default controller({
|
|
36
|
+
login: route.post('/login', {
|
|
37
|
+
async handle(req, res) {
|
|
38
|
+
const user = await db.select().from(users).where({ email: req.body.email })
|
|
39
|
+
|
|
40
|
+
if (!user) {
|
|
41
|
+
res.status(401)
|
|
42
|
+
return { error: 'Invalid credentials' }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Sign the token using process.env.JWT_SECRET
|
|
46
|
+
const token = JWT.signJWT({ userId: user.id }, process.env.JWT_SECRET!, { expiresIn: 604800 })
|
|
47
|
+
|
|
48
|
+
return { token }
|
|
49
|
+
}
|
|
50
|
+
}),
|
|
51
|
+
|
|
52
|
+
verify: route.get('/me', {
|
|
53
|
+
async handle(req, res) {
|
|
54
|
+
try {
|
|
55
|
+
const token = req.headers.authorization?.split(' ')[1]
|
|
56
|
+
|
|
57
|
+
// Verify using process.env.JWT_SECRET
|
|
58
|
+
const decoded = JWT.verifyJWT<{ userId: string }>(token, process.env.JWT_SECRET!)
|
|
59
|
+
|
|
60
|
+
return { userId: decoded.userId }
|
|
61
|
+
} catch (err) {
|
|
62
|
+
res.status(401)
|
|
63
|
+
return { error: 'Invalid or expired token' }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### Class-Based (OOP) Paradigm
|
|
71
|
+
|
|
72
|
+
```typescript filename="src/http/auth/route.ts"
|
|
73
|
+
import { Controller, Post, Get, Body, Req, Res } from 'exisjs/decorators'
|
|
74
|
+
import { JWT } from 'exisjs/auth'
|
|
75
|
+
import { db, users } from '../../db'
|
|
76
|
+
|
|
77
|
+
@Controller('/auth')
|
|
78
|
+
export default class AuthController {
|
|
79
|
+
@Post('/login')
|
|
80
|
+
async login(@Body() body: any, @Res() res: any) {
|
|
81
|
+
const user = await db.select().from(users).where({ email: body.email })
|
|
82
|
+
|
|
83
|
+
if (!user) {
|
|
84
|
+
res.status(401)
|
|
85
|
+
return { error: 'Invalid credentials' }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Sign the token using process.env.JWT_SECRET
|
|
89
|
+
const token = JWT.signJWT({ userId: user.id }, process.env.JWT_SECRET!, { expiresIn: 604800 })
|
|
90
|
+
|
|
91
|
+
return { token }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@Get('/me')
|
|
95
|
+
async verifyMe(@Req() req: any, @Res() res: any) {
|
|
96
|
+
try {
|
|
97
|
+
const token = req.headers.authorization?.split(' ')[1]
|
|
98
|
+
|
|
99
|
+
// Verify using process.env.JWT_SECRET
|
|
100
|
+
const decoded = JWT.verifyJWT<{ userId: string }>(token, process.env.JWT_SECRET!)
|
|
101
|
+
|
|
102
|
+
return { userId: decoded.userId }
|
|
103
|
+
} catch (err) {
|
|
104
|
+
res.status(401)
|
|
105
|
+
return { error: 'Invalid or expired token' }
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Using JWT in Middleware & Gateways
|
|
114
|
+
|
|
115
|
+
While you can verify tokens directly in your routes, ExisJS's JWT integration works well inside Middlewares to protect entire folders using **Gateways**.
|
|
116
|
+
|
|
117
|
+
First, create your middleware outside of your routing tree:
|
|
118
|
+
|
|
119
|
+
```typescript filename="src/middlewares/auth.ts"
|
|
120
|
+
import { JWT } from 'exisjs/auth'
|
|
121
|
+
|
|
122
|
+
export function requireAuth(req: any, res: any, next: any) {
|
|
123
|
+
const token = req.headers.authorization?.split(' ')[1]
|
|
124
|
+
|
|
125
|
+
if (!token) {
|
|
126
|
+
res.status(401).json({ error: 'Missing token' })
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const decoded = JWT.verifyJWT<{ userId: string }>(token, process.env.JWT_SECRET!)
|
|
132
|
+
// Attach the decoded payload to the request for the route to use
|
|
133
|
+
req.user = decoded
|
|
134
|
+
next()
|
|
135
|
+
} catch (err) {
|
|
136
|
+
res.status(401).json({ error: 'Unauthorized' })
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Now, apply it to an entire domain using a Gateway. This prevents you from having to protect every route manually.
|
|
142
|
+
|
|
143
|
+
```typescript filename="src/http/api/gateway.ts"
|
|
144
|
+
import { defineGateway } from 'exisjs/router'
|
|
145
|
+
import { requireAuth } from '../../middlewares/auth'
|
|
146
|
+
|
|
147
|
+
export default defineGateway({
|
|
148
|
+
// Intercepts and protects every route inside the /api folder
|
|
149
|
+
middleware: [requireAuth]
|
|
150
|
+
})
|
|
151
|
+
```
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
---
|
|
2
|
+
nextUrl: "/docs/integrations/redis"
|
|
3
|
+
nextTitle: "Redis"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Database (NoSQL)
|
|
7
|
+
|
|
8
|
+
ExisJS provides native integrations for MongoDB. You do **NOT** need to register the database in your `server.ts` file. The connection pools are lazily initialized when you first execute a query.
|
|
9
|
+
|
|
10
|
+
The framework provides first-class plugins for **Mongoose** (the ODM) and the raw **MongoDB Node.js Driver** (`mongodb`).
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Environment Variables
|
|
15
|
+
|
|
16
|
+
Both MongoDB plugins securely read your connection string from your `.env` file.
|
|
17
|
+
Before using any of the plugins below, ensure you have your MongoDB URI set:
|
|
18
|
+
|
|
19
|
+
```bash filename=".env"
|
|
20
|
+
# Your MongoDB connection string
|
|
21
|
+
MONGODB_URI="mongodb://localhost:27017/my_database"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Mongoose (ODM)
|
|
27
|
+
|
|
28
|
+
The `@exisjs/mongoose` plugin automatically manages your database connection lifecycle.
|
|
29
|
+
|
|
30
|
+
### Basic Setup
|
|
31
|
+
In your `server.ts` file, simply call `configureMongoose({})`:
|
|
32
|
+
|
|
33
|
+
```typescript filename="src/http/server.ts"
|
|
34
|
+
import { exis } from "exisjs";
|
|
35
|
+
import { configureMongoose } from "exisjs/mongoose";
|
|
36
|
+
|
|
37
|
+
export default exis({
|
|
38
|
+
async onStart(app) {
|
|
39
|
+
configureMongoose({});
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Serverless Deployments (Vercel, AWS Lambda)
|
|
45
|
+
|
|
46
|
+
When deploying to serverless platforms, bundlers (like `@vercel/node`) can accidentally create multiple unconnected instances of Mongoose in memory. To guarantee stability in serverless functions, we recommend importing Mongoose directly and passing it as the `client`:
|
|
47
|
+
|
|
48
|
+
```typescript filename="src/http/server.ts"
|
|
49
|
+
import { exis } from "exisjs";
|
|
50
|
+
import mongoose from "mongoose";
|
|
51
|
+
import { configureMongoose } from "exisjs/mongoose";
|
|
52
|
+
|
|
53
|
+
export default exis({
|
|
54
|
+
async onStart(app) {
|
|
55
|
+
configureMongoose({ client: mongoose });
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
## 1. Mongoose (Recommended)
|
|
62
|
+
|
|
63
|
+
Mongoose provides a straight-forward, schema-based solution to model your application data.
|
|
64
|
+
|
|
65
|
+
### Installation
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm install mongoose
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Usage
|
|
72
|
+
|
|
73
|
+
Import the `mongoose` proxy from `exisjs/mongoose`. ExisJS will connect to your database behind the scenes (Mongoose natively buffers operations until connected).
|
|
74
|
+
|
|
75
|
+
```typescript filename="src/models/user.ts"
|
|
76
|
+
import { mongoose } from 'exisjs/mongoose'
|
|
77
|
+
|
|
78
|
+
const userSchema = new mongoose.Schema({
|
|
79
|
+
name: String,
|
|
80
|
+
email: { type: String, unique: true },
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
export const User = mongoose.model('User', userSchema)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Now you can use your `User` model in your routes:
|
|
87
|
+
|
|
88
|
+
#### Functional Paradigm
|
|
89
|
+
|
|
90
|
+
```typescript filename="src/http/users/route.ts"
|
|
91
|
+
import { controller, route } from 'exisjs/router'
|
|
92
|
+
import { User } from '../../models/user'
|
|
93
|
+
|
|
94
|
+
export default controller({
|
|
95
|
+
getUsers: route.get('/', {
|
|
96
|
+
async handle() {
|
|
97
|
+
return await User.find({})
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### Class-Based (OOP) Paradigm
|
|
104
|
+
|
|
105
|
+
```typescript filename="src/http/users/route.ts"
|
|
106
|
+
import { Controller, Get } from 'exisjs/decorators'
|
|
107
|
+
import { User } from '../../models/user'
|
|
108
|
+
|
|
109
|
+
@Controller()
|
|
110
|
+
export default class UsersController {
|
|
111
|
+
@Get('/')
|
|
112
|
+
async getUsers() {
|
|
113
|
+
return await User.find({})
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 2. Raw MongoDB (`mongodb`)
|
|
121
|
+
|
|
122
|
+
If you don't want to use an ODM and prefer working with raw BSON documents, ExisJS provides an integration for the native `mongodb` driver.
|
|
123
|
+
|
|
124
|
+
### Installation
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
npm install mongodb
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Usage
|
|
131
|
+
|
|
132
|
+
Import `mongo` from `exisjs/mongodb`. The first time you interact with it, ExisJS will boot up a MongoClient connection pool using your `MONGODB_URI`.
|
|
133
|
+
|
|
134
|
+
#### Functional Paradigm
|
|
135
|
+
|
|
136
|
+
```typescript filename="src/http/users/route.ts"
|
|
137
|
+
import { controller, route } from 'exisjs/router'
|
|
138
|
+
import { mongo } from 'exisjs/mongodb'
|
|
139
|
+
|
|
140
|
+
export default controller({
|
|
141
|
+
getUsers: route.get('/', {
|
|
142
|
+
async handle() {
|
|
143
|
+
const db = mongo.db('my_database')
|
|
144
|
+
return await db.collection('users').find({}).toArray()
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### Class-Based (OOP) Paradigm
|
|
151
|
+
|
|
152
|
+
```typescript filename="src/http/users/route.ts"
|
|
153
|
+
import { Controller, Get } from 'exisjs/decorators'
|
|
154
|
+
import { mongo } from 'exisjs/mongodb'
|
|
155
|
+
|
|
156
|
+
@Controller()
|
|
157
|
+
export default class UsersController {
|
|
158
|
+
@Get('/')
|
|
159
|
+
async getUsers() {
|
|
160
|
+
const db = mongo.db('my_database')
|
|
161
|
+
return await db.collection('users').find({}).toArray()
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Customizing Connection Options
|
|
167
|
+
|
|
168
|
+
If you need to pass custom options to the MongoDB connection pool (like replica set configs or pool sizes), call `configureMongo(options)` or `configureMongoose(options)` inside your `server.ts` `onStart` hook.
|
|
169
|
+
|
|
170
|
+
```typescript filename="src/http/server.ts"
|
|
171
|
+
import { exis } from 'exisjs'
|
|
172
|
+
import { configureMongoose } from 'exisjs/mongoose'
|
|
173
|
+
|
|
174
|
+
export default exis({
|
|
175
|
+
async onStart() {
|
|
176
|
+
// Initialize with custom connection options
|
|
177
|
+
configureMongoose({
|
|
178
|
+
maxPoolSize: 50,
|
|
179
|
+
wtimeoutMS: 2500,
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
```
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
---
|
|
2
|
+
nextUrl: "/docs/integrations/drizzle"
|
|
3
|
+
nextTitle: "Drizzle ORM"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Observability (Health, Metrics, Tracing)
|
|
7
|
+
|
|
8
|
+
ExisJS includes Observability features out of the box.
|
|
9
|
+
|
|
10
|
+
Instead of locking you into a specific vendor, ExisJS uses a "Bring Your Own Adapter" architecture. We provide middlewares for Health Checks, Metrics (Prometheus/StatsD), and Tracing (OpenTelemetry) that integrate with your vendor SDKs.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. Advanced Health Checks
|
|
15
|
+
|
|
16
|
+
ExisJS includes a `healthCheck` middleware that exposes a `/health` endpoint for Kubernetes liveness/readiness probes or load balancers.
|
|
17
|
+
|
|
18
|
+
It supports parallel asynchronous dependency checks with built-in timeouts.
|
|
19
|
+
|
|
20
|
+
```typescript filename="src/http/server.ts"
|
|
21
|
+
import { exis } from 'exisjs'
|
|
22
|
+
import { healthCheck } from 'exisjs/observability'
|
|
23
|
+
import { db } from 'exisjs/postgres'
|
|
24
|
+
import { redis } from 'exisjs/redis'
|
|
25
|
+
|
|
26
|
+
export default exis({
|
|
27
|
+
middlewares: [
|
|
28
|
+
healthCheck({
|
|
29
|
+
path: '/healthz', // Defaults to '/health'
|
|
30
|
+
checks: [
|
|
31
|
+
{
|
|
32
|
+
name: 'postgres',
|
|
33
|
+
timeoutMs: 3000,
|
|
34
|
+
check: async () => await db`SELECT 1`
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'redis',
|
|
38
|
+
timeoutMs: 2000,
|
|
39
|
+
check: async () => await redis.ping()
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
})
|
|
43
|
+
]
|
|
44
|
+
})
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
When you ping `GET /healthz`, ExisJS will run all checks in parallel.
|
|
48
|
+
- If all checks pass, it returns HTTP `200 OK` with `{ status: "pass", dependencies: { postgres: { status: "up" }, ... } }`.
|
|
49
|
+
- If any check times out or fails, it returns HTTP `503 Service Unavailable` with `{ status: "fail", ... }`.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 2. Metrics (Prometheus / StatsD)
|
|
54
|
+
|
|
55
|
+
Tracking HTTP request durations is a common requirement.
|
|
56
|
+
|
|
57
|
+
ExisJS exposes a `metrics()` middleware adapter. It handles timing calculations and path normalization (e.g., aggregating `/users/1` and `/users/2` into `/users/:id` to prevent Prometheus cardinality explosions).
|
|
58
|
+
|
|
59
|
+
```typescript filename="src/http/server.ts"
|
|
60
|
+
import { exis } from 'exisjs'
|
|
61
|
+
import { metrics } from 'exisjs/observability'
|
|
62
|
+
import promClient from 'prom-client'
|
|
63
|
+
|
|
64
|
+
// 1. Setup your external Prometheus registry
|
|
65
|
+
const httpRequestDurationMicroseconds = new promClient.Histogram({
|
|
66
|
+
name: 'http_request_duration_ms',
|
|
67
|
+
help: 'Duration of HTTP requests in ms',
|
|
68
|
+
labelNames: ['method', 'route', 'code'],
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
export default exis({
|
|
72
|
+
middlewares: [
|
|
73
|
+
// 2. Plug it directly into the ExisJS adapter
|
|
74
|
+
metrics({
|
|
75
|
+
onRequestEnd: ({ method, path, statusCode, durationMs }) => {
|
|
76
|
+
httpRequestDurationMicroseconds
|
|
77
|
+
.labels(method, path, String(statusCode))
|
|
78
|
+
.observe(durationMs)
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
]
|
|
82
|
+
})
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 3. Distributed Tracing (OpenTelemetry)
|
|
88
|
+
|
|
89
|
+
Distributed tracing allows you to follow a single request across multiple microservices.
|
|
90
|
+
|
|
91
|
+
ExisJS provides a `tracing()` adapter designed for OpenTelemetry. It creates spans for every incoming request, injects HTTP statuses, and scrubs sensitive headers (like `authorization`, `cookie`, `session`) before they are sent to your observability platform.
|
|
92
|
+
|
|
93
|
+
```typescript filename="src/http/server.ts"
|
|
94
|
+
import { exis } from 'exisjs'
|
|
95
|
+
import { tracing } from 'exisjs/observability'
|
|
96
|
+
import { trace } from '@opentelemetry/api'
|
|
97
|
+
|
|
98
|
+
// Grab your global OpenTelemetry tracer
|
|
99
|
+
const otelTracer = trace.getTracer('exis-app')
|
|
100
|
+
|
|
101
|
+
export default exis({
|
|
102
|
+
middlewares: [
|
|
103
|
+
tracing({
|
|
104
|
+
// ExisJS calls this hook automatically for every incoming request
|
|
105
|
+
startActiveSpan: (name, metadata, callback) => {
|
|
106
|
+
otelTracer.startActiveSpan(name, (span) => {
|
|
107
|
+
|
|
108
|
+
// ExisJS automatically provides safe, scrubbed metadata
|
|
109
|
+
span.setAttribute('http.method', metadata.method)
|
|
110
|
+
span.setAttribute('http.route', metadata.path)
|
|
111
|
+
|
|
112
|
+
for (const [key, val] of Object.entries(metadata.headers)) {
|
|
113
|
+
span.setAttribute(`http.header.${key}`, String(val))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Pass the span back to ExisJS so it can automatically end it and attach status codes
|
|
117
|
+
callback(span)
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
]
|
|
122
|
+
})
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
With this middleware, your application is instrumented with OpenTelemetry spans.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
nextUrl: "/docs/integrations/posthog"
|
|
3
|
+
nextTitle: "PostHog"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# OpenAI Integration
|
|
7
|
+
|
|
8
|
+
ExisJS provides an integration for OpenAI to harness the power of LLMs.
|
|
9
|
+
|
|
10
|
+
Following our framework philosophy, the `openai` SDK is an optional peer dependency. It remains uninstalled until you explicitly need it, ensuring your core application stays lightweight.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Environment Variables
|
|
15
|
+
|
|
16
|
+
The OpenAI plugin securely reads your API key directly from your `.env` file.
|
|
17
|
+
|
|
18
|
+
Before using the plugin, ensure you have your API key set:
|
|
19
|
+
|
|
20
|
+
```bash filename=".env"
|
|
21
|
+
OPENAI_API_KEY="sk-proj-123456789"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
ExisJS wraps the official `openai` Node.js SDK.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install openai
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
Simply import the `openai` proxy from `exisjs/openai`. ExisJS will boot and cache the client connection the first time you generate a completion.
|
|
39
|
+
|
|
40
|
+
#### Functional Paradigm
|
|
41
|
+
|
|
42
|
+
```typescript filename="src/http/ai/route.ts"
|
|
43
|
+
import { controller, route } from 'exisjs/router'
|
|
44
|
+
import { openai } from 'exisjs/openai'
|
|
45
|
+
|
|
46
|
+
export default controller({
|
|
47
|
+
generateJoke: route.post('/joke', {
|
|
48
|
+
async handle(req) {
|
|
49
|
+
// The openai client lazy-loads here
|
|
50
|
+
const completion = await openai.chat.completions.create({
|
|
51
|
+
messages: [{ role: 'user', content: 'Tell me a programming joke.' }],
|
|
52
|
+
model: 'gpt-4o-mini',
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
success: true,
|
|
57
|
+
joke: completion.choices[0].message.content
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### Class-Based (OOP) Paradigm
|
|
65
|
+
|
|
66
|
+
```typescript filename="src/http/ai/route.ts"
|
|
67
|
+
import { Controller, Post } from 'exisjs/decorators'
|
|
68
|
+
import { openai } from 'exisjs/openai'
|
|
69
|
+
|
|
70
|
+
@Controller('/ai')
|
|
71
|
+
export default class AIController {
|
|
72
|
+
|
|
73
|
+
@Post('/joke')
|
|
74
|
+
async generateJoke() {
|
|
75
|
+
// The openai client magically lazy-loads here!
|
|
76
|
+
const completion = await openai.chat.completions.create({
|
|
77
|
+
messages: [{ role: 'user', content: 'Tell me a programming joke.' }],
|
|
78
|
+
model: 'gpt-4o-mini',
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
success: true,
|
|
83
|
+
joke: completion.choices[0].message.content
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Customizing Connection Options
|
|
92
|
+
|
|
93
|
+
If you need to pass custom options to the OpenAI Client (e.g., overriding the `baseURL` to use an OpenAI-compatible open-source model via Ollama or vLLM), call `configureOpenAI(options)` inside your `server.ts` `onStart` hook.
|
|
94
|
+
|
|
95
|
+
```typescript filename="src/http/server.ts"
|
|
96
|
+
import { exis } from 'exisjs'
|
|
97
|
+
import { configureOpenAI } from 'exisjs/openai'
|
|
98
|
+
|
|
99
|
+
export default exis({
|
|
100
|
+
async onStart() {
|
|
101
|
+
// Override the baseURL to point to a local Ollama instance
|
|
102
|
+
configureOpenAI({
|
|
103
|
+
baseURL: 'http://localhost:11434/v1',
|
|
104
|
+
apiKey: 'ollama' // Local LLMs often just need a dummy key
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
```
|