diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..c34d384 --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +DATABASE_URL=postgres://user:password@127.0.0.1:5432/postgres +REDIS_URL=redis://127.0.0.1:6379/ +PORT=8080 +HOST=127.0.0.1 \ No newline at end of file diff --git a/.gitignore b/.gitignore index dae5e8d..f79e769 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules env yarn-error.log -dist \ No newline at end of file +dist +.env \ No newline at end of file diff --git a/README.md b/README.md index 054235b..cef14b9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# fastify-drizzle-quick-start +# fastify-jsdoc-quick-start -Quick start for Fastify, TypeScript, ESlint, Prettier and DrizzleORM +Quick start for Fastify, JSDoc, ESlint, Prettier and DrizzleORM ## Quick Start @@ -8,7 +8,7 @@ Quick start for Fastify, TypeScript, ESlint, Prettier and DrizzleORM ### Comes equipped with - Dockerfile including postgres and redis -- TypeScript +- JSDoc type annotations - XO - Prettier - DrizzleORM @@ -30,5 +30,9 @@ Quick start for Fastify, TypeScript, ESlint, Prettier and DrizzleORM 4. Run `yarn dev` to start the dev server 5. Make your changes 6. Run `yarn lint` to lint your code -7. Run `yarn build` to build the project -8. Run `yarn start` to start the production server +7. Run `yarn start` to start the production server + + +# Credits + +Originally cloned from https://github.com/Looskie/fastify-drizzle-quick-start and transformed to use JSDoc over Typescript \ No newline at end of file diff --git a/package.json b/package.json index b53c35c..c792fcb 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "fastify-drizzle-quick-start", "version": "1.0.0", "description": "Quick start for Fastify, TypeScript, ESlint, Prettier and DrizzleORM", + "type": "module", "main": "index.js", "repository": "https://github.com/Looskie/fastify-drizzle-quick-start.git", "author": "Cody Miller <50378828+Looskie@users.noreply.github.com>", @@ -12,7 +13,7 @@ "prettier": "prettier --write .", "migrate": "drizzle-kit generate:pg", "build": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json", - "start": "node dist/index.js" + "start": "node src/index.js" }, "devDependencies": { "@types/cli-color": "^2.0.6", diff --git a/src/db/index.ts b/src/db/index.js similarity index 80% rename from src/db/index.ts rename to src/db/index.js index 3b857b1..e1e60b0 100644 --- a/src/db/index.ts +++ b/src/db/index.js @@ -1,11 +1,12 @@ -import * as schema from "@api/db/schemas"; -import { env, Logger } from "@api/utils"; +import * as schema from "./schemas.js"; +import { env, Logger } from "../utils/index.js"; import { drizzle } from "drizzle-orm/node-postgres"; import { migrate } from "drizzle-orm/node-postgres/migrator"; -import { Pool } from "pg"; +import pgpkg from 'pg'; +const { Pool } = pgpkg; -// eslint-disable-next-line import/no-mutable-exports -export let db: ReturnType>; +/** @type {ReturnType>} */ +export let db; export const initDb = async () => { const pool = await new Pool({ diff --git a/src/db/schemas.ts b/src/db/schemas.js similarity index 100% rename from src/db/schemas.ts rename to src/db/schemas.js diff --git a/src/index.ts b/src/index.js similarity index 75% rename from src/index.ts rename to src/index.js index 1f39f2f..22e9a6b 100644 --- a/src/index.ts +++ b/src/index.js @@ -1,10 +1,9 @@ -import { initDb } from "@api/db"; -import { testRoutes } from "@api/routes"; -import { env, Logger, Redis } from "@api/utils"; +import { initDb } from "./db/index.js"; +import { testRoutes } from "./routes/index.js"; +import { env, Logger, Redis } from "./utils/index.js"; import fastify from "fastify"; -import { middleware } from "./modules/middleware"; +import { middleware } from "./modules/middleware.js"; -// eslint-disable-next-line @typescript-eslint/naming-convention const API_VERSION = "v1"; export const main = async () => { diff --git a/src/modules/middleware.js b/src/modules/middleware.js new file mode 100644 index 0000000..23700ba --- /dev/null +++ b/src/modules/middleware.js @@ -0,0 +1,18 @@ +/** @typedef {import("fastify").FastifyInstance} FastifyInstance */ +import fp from "fastify-plugin"; +import { Redis } from "../utils/index.js"; +import { db } from "../db/index.js"; + +const middleware = fp( + /** + * @param {FastifyInstance} fastify + * @param {unknown} _options + */ + async (fastify, _options) => { + fastify.addHook("onRequest", async (request) => { + request.redis = Redis; + request.db = db; + }); +}); + +export { middleware }; diff --git a/src/modules/middleware.ts b/src/modules/middleware.ts deleted file mode 100644 index fdcff5f..0000000 --- a/src/modules/middleware.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { FastifyInstance } from "fastify"; -import fp from "fastify-plugin"; -import { Redis } from "../utils"; -import { db } from "../db"; - -const middleware = fp(async (fastify: FastifyInstance, _options: unknown) => { - fastify.addHook("onRequest", async (request) => { - request.redis = Redis; - request.db = db; - }); -}); - -export { middleware }; diff --git a/src/routes/index.js b/src/routes/index.js new file mode 100644 index 0000000..22111de --- /dev/null +++ b/src/routes/index.js @@ -0,0 +1 @@ +export * from "./test.js"; diff --git a/src/routes/index.ts b/src/routes/index.ts deleted file mode 100644 index 481faba..0000000 --- a/src/routes/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./test"; diff --git a/src/routes/test.ts b/src/routes/test.js similarity index 51% rename from src/routes/test.ts rename to src/routes/test.js index dc3fc79..0fa1f83 100644 --- a/src/routes/test.ts +++ b/src/routes/test.js @@ -1,6 +1,12 @@ -import type { FastifyInstance } from "fastify"; +/** @typedef {import("fastify").FastifyInstance} FastifyInstance */ -export const testRoutes = (fastify: FastifyInstance, _: unknown, done: () => void) => { +/** + * + * @param {FastifyInstance} fastify + * @param {unknown} _ + * @param {() => void} done + */ +export const testRoutes = (fastify, _, done) => { fastify.get("/", async (request, response) => { const visits = Number(await request.redis.get("visits")); diff --git a/src/utils/env.ts b/src/utils/env.js similarity index 76% rename from src/utils/env.ts rename to src/utils/env.js index e3e19a9..7677315 100644 --- a/src/utils/env.ts +++ b/src/utils/env.js @@ -1,6 +1,4 @@ -/* eslint-disable @typescript-eslint/naming-convention */ import { z } from "zod"; -// eslint-disable-next-line import/no-unassigned-import import "dotenv/config"; const envSchema = z.object({ diff --git a/src/utils/index.js b/src/utils/index.js new file mode 100644 index 0000000..a064837 --- /dev/null +++ b/src/utils/index.js @@ -0,0 +1,3 @@ +export * from "./env.js"; +export * from "./logger.js"; +export * from "./redis.js"; diff --git a/src/utils/index.ts b/src/utils/index.ts deleted file mode 100644 index 3a28a91..0000000 --- a/src/utils/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./env"; -export * from "./logger"; -export * from "./redis"; diff --git a/src/utils/logger.js b/src/utils/logger.js new file mode 100644 index 0000000..2aab5e0 --- /dev/null +++ b/src/utils/logger.js @@ -0,0 +1,32 @@ +import clc from "cli-color"; + +class Logger { + /** + * + * @param {string} prefix + * @param {string} message + */ + static info(prefix, message) { + console.log(`[${clc.cyan(prefix)}] ${message}`); + } + + /** + * + * @param {string} prefix + * @param {string} message + */ + static error(prefix, message) { + console.log(`[${clc.red(prefix)}] ${message}`); + } + + /** + * + * @param {string} prefix + * @param {string} message + */ + static success(prefix, message) { + console.log(`[${clc.green(prefix)}] ${message}`); + } +} + +export { Logger }; diff --git a/src/utils/logger.ts b/src/utils/logger.ts deleted file mode 100644 index 6019fcd..0000000 --- a/src/utils/logger.ts +++ /dev/null @@ -1,17 +0,0 @@ -import clc from "cli-color"; - -class Logger { - public static info(prefix: string, message: string) { - console.log(`[${clc.cyan(prefix)}] ${message}`); - } - - public static error(prefix: string, message: string) { - console.log(`[${clc.red(prefix)}] ${message}`); - } - - public static success(prefix: string, message: string) { - console.log(`[${clc.green(prefix)}] ${message}`); - } -} - -export { Logger }; diff --git a/src/utils/redis.js b/src/utils/redis.js new file mode 100644 index 0000000..926c53f --- /dev/null +++ b/src/utils/redis.js @@ -0,0 +1,91 @@ +import * as redis from "redis"; +import { env, Logger } from "./index.js"; + +/** @typedef {"session" | "visits"} RedisPrefixes */ +/** @typedef {`${RedisPrefixes}:${string | number}` | RedisPrefixes} RedisKey */ + +class Redis { + /** + * @type {ReturnType} + * @public + */ + static redis; + + /** @public */ + static async initialize() { + this.redis = redis.createClient({ + url: env.REDIS_URL, + }); + + this.redis.on("error", (error) => { + Logger.error("INIT", "Failed to connect to redis " + String(error)); + throw new Error("Failed to connect to redis"); + }); + + this.redis.on("connect", () => { + Logger.info("INIT", "Connected to redis"); + }); + + await this.redis.connect(); + } + + /** + * @param {RedisKey} key Key of the value to set + * @param {string} value Value to set + * @param {?redis.SetOptions} options Options for the value to set + * @public + * + * @example + * await Redis.set("session:user_xxx", new Date().toISOString()); + * await Redis.set("visits", "1"); + */ + static async set(key, value, options) { + await this.redis.set(key, value, options); + } + + /** + * + * @param {RedisKey} key + * @public + */ + static async incr(key) { + await this.redis.incr(key); + } + + /** + * + * @param {RedisKey} key + * @public + */ + static async decr(key) { + await this.redis.decr(key); + } + + /** + * @template T + * + * @param {RedisKey} key Key of the value to get + * @example + * const value = await Redis.get("session:user_xxx"); + * const visits = await Redis.get("visits"); + * + * @returns {T | undefined} + */ + static async get(key) { + return this.redis.get(key); + } + + /** + * + * @param {RedisKey} key Key of the value to delete + * @public + * @example + * await Redis.del("session:user_xxx"); + * await Redis.del("visits"); + */ + static async del(key) { + await this.redis.del(key); + } +} + +export { Redis }; diff --git a/src/utils/redis.ts b/src/utils/redis.ts deleted file mode 100644 index 4978b4f..0000000 --- a/src/utils/redis.ts +++ /dev/null @@ -1,72 +0,0 @@ -import * as redis from "redis"; -import { env, Logger } from "@api/utils"; - -type RedisPrefixes = "session" | "visits"; -type RedisKey = `${RedisPrefixes}:${string | number}` | RedisPrefixes; - -class Redis { - public static redis: ReturnType; - - public static async initialize() { - this.redis = redis.createClient({ - url: env.REDIS_URL, - }); - - this.redis.on("error", (error) => { - Logger.error("INIT", "Failed to connect to redis " + String(error)); - throw new Error("Failed to connect to redis"); - }); - - this.redis.on("connect", () => { - Logger.info("INIT", "Connected to redis"); - }); - - await this.redis.connect(); - } - - /** - * - * @param key Key of the value to set - * @param value Value to set - * @param options Options for the value to set - * - * @example - * await Redis.set("session:user_xxx", new Date().toISOString()); - * await Redis.set("visits", "1"); - */ - public static async set(key: RedisKey, value: string, options?: redis.SetOptions) { - await this.redis.set(key, value, options); - } - - public static async incr(key: RedisKey) { - await this.redis.incr(key); - } - - public static async decr(key: RedisKey) { - await this.redis.decr(key); - } - - /** - * - * @param key Key of the value to get - * @example - * const value = await Redis.get("session:user_xxx"); - * const visits = await Redis.get("visits"); - */ - public static async get(key: RedisKey) { - return this.redis.get(key) as T | undefined; - } - - /** - * - * @param key Key of the value to delete - * @example - * await Redis.del("session:user_xxx"); - * await Redis.del("visits"); - */ - public static async del(key: RedisKey) { - await this.redis.del(key); - } -} - -export { Redis, type RedisPrefixes }; diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index f40eb4e..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "compilerOptions": { - "module": "CommonJS", - "target": "ESNext", - "strict": true, - "useUnknownInCatchVariables": true, - "noImplicitOverride": true, - "noFallthroughCasesInSwitch": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "outDir": "dist", - "esModuleInterop": true, - "exactOptionalPropertyTypes": true, - "noImplicitReturns": true, - "noUncheckedIndexedAccess": true, - "baseUrl": ".", - "paths": { - "@api/*": ["src/*"] - }, - "skipLibCheck": true - }, - "exclude": ["node_modules", "./dist/**/*"] -}