Skip to content

Commit bd35e11

Browse files
committed
Merge branch 'feat/example-app'
2 parents 9c75d92 + 0775190 commit bd35e11

19 files changed

+3712
-45
lines changed

docker-compose.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,16 @@ services:
33
image: redis:8-alpine
44
ports:
55
- '6379:6379'
6+
7+
redis-cluster:
8+
image: redis/redis-stack:latest
9+
command: |
10+
sh -c '
11+
redis-server --port 6380 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes --loadmodule /opt/redis-stack/lib/redisearch.so --loadmodule /opt/redis-stack/lib/rejson.so --protected-mode no --bind 0.0.0.0 &
12+
sleep 5
13+
redis-cli -p 6380 cluster meet 127.0.0.1 6380 || true
14+
redis-cli -p 6380 cluster addslots $(seq 0 16383 | tr "\n" " ") || true
15+
wait
16+
'
17+
ports:
18+
- '6380:6380'

examples/full/eslint.config.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import baseConfig from '../../eslint.config.mjs';
2+
3+
export default [...baseConfig];

examples/full/package.json

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
{
2+
"name": "@examples/full",
3+
"version": "0.0.1",
4+
"private": true,
5+
"nx": {
6+
"targets": {
7+
"build": {
8+
"executor": "nx:run-commands",
9+
"outputs": [
10+
"{projectRoot}/dist"
11+
],
12+
"options": {
13+
"command": "webpack-cli build",
14+
"args": [
15+
"--node-env=production"
16+
],
17+
"cwd": "examples/full"
18+
},
19+
"configurations": {
20+
"development": {
21+
"args": [
22+
"--node-env=development"
23+
]
24+
}
25+
}
26+
},
27+
"prune-lockfile": {
28+
"dependsOn": [
29+
"build"
30+
],
31+
"cache": true,
32+
"executor": "@nx/js:prune-lockfile",
33+
"outputs": [
34+
"{workspaceRoot}/examples/full/dist/package.json",
35+
"{workspaceRoot}/examples/full/dist/pnpm-lock.yaml"
36+
],
37+
"options": {
38+
"buildTarget": "build"
39+
}
40+
},
41+
"copy-workspace-modules": {
42+
"dependsOn": [
43+
"build"
44+
],
45+
"cache": true,
46+
"outputs": [
47+
"{workspaceRoot}/examples/full/dist/workspace_modules"
48+
],
49+
"executor": "@nx/js:copy-workspace-modules",
50+
"options": {
51+
"buildTarget": "build"
52+
}
53+
},
54+
"prune": {
55+
"dependsOn": [
56+
"prune-lockfile",
57+
"copy-workspace-modules"
58+
],
59+
"executor": "nx:noop"
60+
},
61+
"serve": {
62+
"continuous": true,
63+
"executor": "@nx/js:node",
64+
"defaultConfiguration": "development",
65+
"dependsOn": [
66+
"build"
67+
],
68+
"options": {
69+
"buildTarget": "@examples/full:build",
70+
"runBuildTargetDependencies": false
71+
},
72+
"configurations": {
73+
"development": {
74+
"buildTarget": "@examples/full:build:development"
75+
},
76+
"production": {
77+
"buildTarget": "@examples/full:build:production"
78+
}
79+
}
80+
}
81+
}
82+
},
83+
"dependencies": {
84+
"@nestjs-redis/kit": "*",
85+
"redis": "^5.0.0"
86+
}
87+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Controller, Get, UseGuards } from '@nestjs/common';
2+
import { ThrottlerGuard } from '@nestjs/throttler';
3+
import { AppService } from './app.service';
4+
5+
@Controller()
6+
export class AppController {
7+
constructor(private readonly appService: AppService) {}
8+
9+
@Get('date')
10+
getData() {
11+
return this.appService.getValue('test-date');
12+
}
13+
14+
@Get('date/set')
15+
@UseGuards(ThrottlerGuard)
16+
async setData() {
17+
return this.appService.setValue('test-date', new Date().toISOString());
18+
}
19+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// app.module.ts
2+
import { Module } from '@nestjs/common';
3+
import { TerminusModule } from '@nestjs/terminus';
4+
import { ThrottlerModule, seconds } from '@nestjs/throttler';
5+
import {
6+
RedisHealthIndicator,
7+
RedisModule,
8+
RedisThrottlerStorage,
9+
RedisToken,
10+
RedlockModule,
11+
} from '@nestjs-redis/kit';
12+
import type { RedisClientType } from 'redis';
13+
import { AppController } from './app.controller';
14+
import { AppService } from './app.service';
15+
import { HealthController } from './health.controller';
16+
17+
@Module({
18+
imports: [
19+
// Client
20+
RedisModule.forRoot({
21+
isGlobal: true,
22+
options: { url: 'redis://localhost:6379' },
23+
}),
24+
RedisModule.forRoot({
25+
isGlobal: true,
26+
options: { url: 'redis://localhost:6380' },
27+
connectionName: 'cluster',
28+
}),
29+
30+
// Locking
31+
RedlockModule.forRootAsync({
32+
inject: [RedisToken()],
33+
useFactory: (redis: RedisClientType) => ({
34+
clients: [redis],
35+
redlockConfig: { retryDelayMs: 100, retryCount: 2 },
36+
}),
37+
}),
38+
39+
// Throttling
40+
ThrottlerModule.forRootAsync({
41+
inject: [RedisToken()],
42+
useFactory: (redis: RedisClientType) => ({
43+
throttlers: [{ limit: 5111, ttl: seconds(60) }],
44+
storage: new RedisThrottlerStorage(redis),
45+
}),
46+
}),
47+
48+
// Health checks
49+
TerminusModule,
50+
],
51+
controllers: [AppController, HealthController],
52+
providers: [RedisHealthIndicator, AppService],
53+
})
54+
export class AppModule {}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// app.service.ts
2+
import { Injectable } from '@nestjs/common';
3+
import { InjectRedis, Redlock } from '@nestjs-redis/kit';
4+
import type { RedisClientType } from 'redis';
5+
6+
@Injectable()
7+
export class AppService {
8+
constructor(@InjectRedis() private readonly redis: RedisClientType) {}
9+
10+
@Redlock(['locks:test'], 100)
11+
async setValue(key: string, value: string) {
12+
await new Promise((resolve) => setTimeout(resolve, 2000));
13+
await this.redis.set(key, value);
14+
}
15+
16+
async getValue(key: string) {
17+
return this.redis.get(key);
18+
}
19+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Controller, Get } from '@nestjs/common';
2+
import { HealthCheck, HealthCheckService } from '@nestjs/terminus';
3+
import { InjectRedis, RedisHealthIndicator } from '@nestjs-redis/kit';
4+
import type { RedisClientType, RedisClusterType } from 'redis';
5+
6+
@Controller('health')
7+
export class HealthController {
8+
constructor(
9+
private readonly health: HealthCheckService,
10+
private readonly redis: RedisHealthIndicator,
11+
@InjectRedis() private readonly redisClient: RedisClientType,
12+
@InjectRedis('cluster')
13+
private readonly redisClusterClient: RedisClusterType,
14+
) {}
15+
16+
@Get()
17+
@HealthCheck()
18+
check() {
19+
return this.health.check([
20+
() =>
21+
this.redis.isHealthy('redis-client', {
22+
client: this.redisClient,
23+
}),
24+
() =>
25+
this.redis.isHealthy('redis-cluster', {
26+
client: this.redisClusterClient,
27+
}),
28+
]);
29+
}
30+
}

examples/full/src/assets/.gitkeep

Whitespace-only changes.

examples/full/src/main.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* This is not a production server yet!
3+
* This is only a minimal backend to get started.
4+
*/
5+
import { Logger } from '@nestjs/common';
6+
import { NestFactory } from '@nestjs/core';
7+
import { AppModule } from './app/app.module';
8+
9+
async function bootstrap() {
10+
const app = await NestFactory.create(AppModule);
11+
const globalPrefix = 'api';
12+
app.setGlobalPrefix(globalPrefix);
13+
const port = process.env.PORT || 3000;
14+
await app.listen(port);
15+
Logger.log(
16+
`🚀 Application is running on: http://localhost:${port}/${globalPrefix}`,
17+
);
18+
}
19+
20+
bootstrap();

examples/full/tsconfig.app.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"extends": "../../tsconfig.base.json",
3+
"compilerOptions": {
4+
"outDir": "dist",
5+
"types": ["node"],
6+
"rootDir": "src",
7+
"tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo",
8+
"experimentalDecorators": true,
9+
"emitDecoratorMetadata": true,
10+
"target": "es2021"
11+
},
12+
"include": ["src/**/*.ts"],
13+
"exclude": ["eslint.config.js", "eslint.config.cjs", "eslint.config.mjs"],
14+
"references": [
15+
{
16+
"path": "../../packages/kit/tsconfig.lib.json"
17+
}
18+
]
19+
}

0 commit comments

Comments
 (0)