initial commit
This commit is contained in:
parent
b5f39cdecc
commit
0e16979232
4
.env.example
Normal file
4
.env.example
Normal file
@ -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
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
node_modules
|
node_modules
|
||||||
env
|
env
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
dist
|
dist
|
||||||
|
.env
|
14
README.md
14
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
|
## Quick Start
|
||||||
@ -8,7 +8,7 @@ Quick start for Fastify, TypeScript, ESlint, Prettier and DrizzleORM
|
|||||||
### Comes equipped with
|
### Comes equipped with
|
||||||
|
|
||||||
- Dockerfile including postgres and redis
|
- Dockerfile including postgres and redis
|
||||||
- TypeScript
|
- JSDoc type annotations
|
||||||
- XO
|
- XO
|
||||||
- Prettier
|
- Prettier
|
||||||
- DrizzleORM
|
- DrizzleORM
|
||||||
@ -30,5 +30,9 @@ Quick start for Fastify, TypeScript, ESlint, Prettier and DrizzleORM
|
|||||||
4. Run `yarn dev` to start the dev server
|
4. Run `yarn dev` to start the dev server
|
||||||
5. Make your changes
|
5. Make your changes
|
||||||
6. Run `yarn lint` to lint your code
|
6. Run `yarn lint` to lint your code
|
||||||
7. Run `yarn build` to build the project
|
7. Run `yarn start` to start the production server
|
||||||
8. 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
|
@ -2,6 +2,7 @@
|
|||||||
"name": "fastify-drizzle-quick-start",
|
"name": "fastify-drizzle-quick-start",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Quick start for Fastify, TypeScript, ESlint, Prettier and DrizzleORM",
|
"description": "Quick start for Fastify, TypeScript, ESlint, Prettier and DrizzleORM",
|
||||||
|
"type": "module",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": "https://github.com/Looskie/fastify-drizzle-quick-start.git",
|
"repository": "https://github.com/Looskie/fastify-drizzle-quick-start.git",
|
||||||
"author": "Cody Miller <50378828+Looskie@users.noreply.github.com>",
|
"author": "Cody Miller <50378828+Looskie@users.noreply.github.com>",
|
||||||
@ -12,7 +13,7 @@
|
|||||||
"prettier": "prettier --write .",
|
"prettier": "prettier --write .",
|
||||||
"migrate": "drizzle-kit generate:pg",
|
"migrate": "drizzle-kit generate:pg",
|
||||||
"build": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
|
"build": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
|
||||||
"start": "node dist/index.js"
|
"start": "node src/index.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/cli-color": "^2.0.6",
|
"@types/cli-color": "^2.0.6",
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import * as schema from "@api/db/schemas";
|
import * as schema from "./schemas.js";
|
||||||
import { env, Logger } from "@api/utils";
|
import { env, Logger } from "../utils/index.js";
|
||||||
import { drizzle } from "drizzle-orm/node-postgres";
|
import { drizzle } from "drizzle-orm/node-postgres";
|
||||||
import { migrate } from "drizzle-orm/node-postgres/migrator";
|
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
|
/** @type {ReturnType<typeof drizzle<typeof schema>>} */
|
||||||
export let db: ReturnType<typeof drizzle<typeof schema>>;
|
export let db;
|
||||||
|
|
||||||
export const initDb = async () => {
|
export const initDb = async () => {
|
||||||
const pool = await new Pool({
|
const pool = await new Pool({
|
@ -1,10 +1,9 @@
|
|||||||
import { initDb } from "@api/db";
|
import { initDb } from "./db/index.js";
|
||||||
import { testRoutes } from "@api/routes";
|
import { testRoutes } from "./routes/index.js";
|
||||||
import { env, Logger, Redis } from "@api/utils";
|
import { env, Logger, Redis } from "./utils/index.js";
|
||||||
import fastify from "fastify";
|
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";
|
const API_VERSION = "v1";
|
||||||
|
|
||||||
export const main = async () => {
|
export const main = async () => {
|
18
src/modules/middleware.js
Normal file
18
src/modules/middleware.js
Normal file
@ -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 };
|
@ -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 };
|
|
1
src/routes/index.js
Normal file
1
src/routes/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./test.js";
|
@ -1 +0,0 @@
|
|||||||
export * from "./test";
|
|
@ -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) => {
|
fastify.get("/", async (request, response) => {
|
||||||
const visits = Number(await request.redis.get("visits"));
|
const visits = Number(await request.redis.get("visits"));
|
||||||
|
|
@ -1,6 +1,4 @@
|
|||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
// eslint-disable-next-line import/no-unassigned-import
|
|
||||||
import "dotenv/config";
|
import "dotenv/config";
|
||||||
|
|
||||||
const envSchema = z.object({
|
const envSchema = z.object({
|
3
src/utils/index.js
Normal file
3
src/utils/index.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from "./env.js";
|
||||||
|
export * from "./logger.js";
|
||||||
|
export * from "./redis.js";
|
@ -1,3 +0,0 @@
|
|||||||
export * from "./env";
|
|
||||||
export * from "./logger";
|
|
||||||
export * from "./redis";
|
|
32
src/utils/logger.js
Normal file
32
src/utils/logger.js
Normal file
@ -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 };
|
@ -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 };
|
|
91
src/utils/redis.js
Normal file
91
src/utils/redis.js
Normal file
@ -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<typeof redis.createClient>}
|
||||||
|
* @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 };
|
@ -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<typeof redis.createClient>;
|
|
||||||
|
|
||||||
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<T = string>(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 };
|
|
@ -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/**/*"]
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user