Skip to content

Commit

Permalink
feat: add redis cache
Browse files Browse the repository at this point in the history
  • Loading branch information
alexcastrodev committed Oct 2, 2023
1 parent b4aa73d commit 3fefea6
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 10 deletions.
9 changes: 9 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Deno >= v1.9.2
- [Vercel](https://vercel.com/)
- GitHub API v4
- Docker and Docker compose (opcional)

## Local Run

Expand All @@ -26,6 +27,14 @@ Run local server.
deno task start
```

You can enable the Redis if you want, but it's not mandatory.

```sh
docker compose up -d
```

Rename `env-example` to `.env`, and change ENABLE_REDIS to true

Open localhost from your browser.

http://localhost:8080/?username=ryo-ma
Expand Down
29 changes: 19 additions & 10 deletions api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { GithubRepositoryService } from "../src/Repository/GithubRepository.ts";
import { GithubApiService } from "../src/Services/GithubApiService.ts";
import { ServiceError } from "../src/Types/index.ts";
import { ErrorPage } from "../src/pages/Error.ts";
import { cacheProvider } from "../src/config/cache.ts";

const serviceProvider = new GithubApiService();
const client = new GithubRepositoryService(serviceProvider).repository;
Expand Down Expand Up @@ -75,17 +76,25 @@ async function app(req: Request): Promise<Response> {
},
);
}
const userInfo = await client.requestUserInfo(username);
if (userInfo instanceof ServiceError) {
return new Response(
ErrorPage({ error: userInfo, username }).render(),
{
status: userInfo.code,
headers: new Headers({ "Content-Type": "text" }),
},
);
}
const userKeyCache = ["v1", username].join("-");
const userInfoCached = await cacheProvider.get(userKeyCache) || "{}";
let userInfo = JSON.parse(userInfoCached);
const hasCache = !!Object.keys(userInfo).length;

if (!hasCache) {
const userResponseInfo = await client.requestUserInfo(username);
if (userResponseInfo instanceof ServiceError) {
return new Response(
ErrorPage({ error: userInfo, username }).render(),
{
status: userResponseInfo.code,
headers: new Headers({ "Content-Type": "text" }),
},
);
}
userInfo = userResponseInfo;
await cacheProvider.set(userKeyCache, JSON.stringify(userInfo));
}
// Success Response
return new Response(
new Card(
Expand Down
6 changes: 6 additions & 0 deletions deps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ import {
spy,
} from "https://deno.land/std@0.203.0/testing/mock.ts";

export {
type Bulk,
connect,
type Redis,
} from "https://deno.land/x/redis@v0.31.0/mod.ts";

import { CONSTANTS } from "./src/utils.ts";

const baseURL = Deno.env.get("GITHUB_API") || CONSTANTS.DEFAULT_GITHUB_API;
Expand Down
7 changes: 7 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version: "3"
services:
redis:
container_name: trophy-redis
image: redis:latest
ports:
- "6379:6379"
8 changes: 8 additions & 0 deletions env-example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
GITHUB_TOKEN1=
GITHUB_TOKEN2=
GITHUB_API=https://api.github.com/graphql
ENABLE_REDIS=
REDIS_PORT=10709
REDIS_HOST=
REDIS_USERNAME=
REDIS_PASSWORD=
70 changes: 70 additions & 0 deletions src/config/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Bulk, connect, Redis } from "../../deps.ts";
import { Logger } from "../Helpers/Logger.ts";

const enableCache = Deno.env.get("ENABLE_REDIS") || false;

class CacheProvider {
private static instance: CacheProvider;
public client: Redis | null = null;

private constructor() {}

static getInstance(): CacheProvider {
if (!CacheProvider.instance) {
CacheProvider.instance = new CacheProvider();
}
return CacheProvider.instance;
}

async connect(): Promise<void> {
if (!enableCache) return;
this.client = await connect({
hostname: Deno.env.get("REDIS_HOST") || "",
port: Number(Deno.env.get("REDIS_PORT")) || 6379,
username: Deno.env.get("REDIS_USERNAME") || "",
password: Deno.env.get("REDIS_PASSWORD") || "",
});
}

async get(key: string): Promise<Bulk | undefined> {
if (!enableCache) return undefined;

try {
if (!this.client) {
await this.connect();
}

return await this.client?.get(key);
} catch {
return undefined;
}
}

async set(key: string, value: string): Promise<void> {
if (!enableCache) return;

try {
if (!this.client) {
await this.connect();
}
await this.client?.set(key, value);
} catch (e) {
Logger.error(`Failed to set cache: ${e.message}`);
}
}

async del(key: string): Promise<void> {
if (!enableCache) return;

try {
if (!this.client) {
await this.connect();
}
await this.client?.del(key);
} catch (e) {
Logger.error(`Failed to delete cache: ${e.message}`);
}
}
}

export const cacheProvider = CacheProvider.getInstance();

0 comments on commit 3fefea6

Please sign in to comment.