Skip to content

Commit 1db5c68

Browse files
committed
feat: change routes strucure
feat: use @fastify/env access env variables
1 parent 7ef497d commit 1db5c68

File tree

16 files changed

+293
-174
lines changed

16 files changed

+293
-174
lines changed

app.js

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
import Fastify from "fastify";
2-
import Autoload from "@fastify/autoload";
2+
import AutoLoad from "@fastify/autoload";
3+
import Env from "@fastify/env";
4+
35
import pino from "pino";
46
import pretty from "pino-pretty";
5-
import { dirname,join } from "node:path";
6-
import { fileURLToPath } from "node:url";
7-
import registerRoutes from "./routes/routes.js";
8-
import registerBooksRoutes from "./routes/booksRoutes.js";
9-
10-
import * as dotenv from "dotenv";
11-
dotenv.config();
12-
7+
import { join } from "desm";
138

149
// logger
1510
const stream = pretty({
@@ -31,20 +26,38 @@ app.decorate("authenticate", async (request, reply) => {
3126
}
3227
});
3328

34-
const __filename = fileURLToPath(import.meta.url);
35-
const __dirname = dirname(__filename);
36-
await app.register(Autoload, {
37-
dir: join(__dirname, "plugins"),
29+
// env
30+
const envSchema = {
31+
type: "object",
32+
required: ["POSTGRES_URL", "JWT_SECRET"],
33+
properties: {
34+
POSTGRES_URL: { type: "string" },
35+
JWT_SECRET: { type: "string" },
36+
NODE_ENV: { type: "string", default: "development" },
37+
},
38+
};
39+
await app.register(Env, {
40+
confKey: "secrets",
41+
schema: envSchema,
42+
dotenv: true,
43+
});
44+
45+
await app.register(AutoLoad, {
46+
dir: join(import.meta.url, "plugins"),
3847
dirNameRoutePrefix: false,
3948
ignorePattern: /.*.no-load\.js/,
4049
indexPattern: /^no$/i,
4150
});
4251

43-
// routes
44-
await registerRoutes(app);
45-
46-
// books routes
47-
await registerBooksRoutes(app);
52+
// register routes
53+
await app.register(import("./routes/routes.js"));
54+
await app.register(import("./routes/books/routes.js"));
55+
await app.register(import("./routes/feeds/routes.js"));
56+
await app.register(import("./routes/movies/routes.js"));
57+
await app.register(import("./routes/music/routes.js"));
58+
await app.register(import("./routes/musicals/routes.js"));
59+
await app.register(import("./routes/series/routes.js"));
60+
await app.register(import("./routes/words/routes.js"));
4861

4962
// start server
5063
if (process.env.NODE_ENV === "development") {

package.json

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,32 @@
55
"devDependencies": {
66
"@biomejs/biome": "^1.9.3",
77
"cheerio": "^1.0.0",
8-
"dotenv": "^16.4.5",
98
"nodemon": "^3.1.7",
109
"pino": "^9.4.0",
1110
"pino-pretty": "^11.2.2"
1211
},
1312
"dependencies": {
14-
"@fastify/autoload": "^6.0.1",
13+
"@fastify/autoload": "^6.0.2",
1514
"@fastify/compress": "^8.0.1",
1615
"@fastify/cors": "^10.0.1",
16+
"@fastify/env": "^5.0.1",
1717
"@fastify/formbody": "^8.0.1",
1818
"@fastify/helmet": "^12.0.1",
1919
"@fastify/jwt": "^9.0.1",
2020
"@fastify/postgres": "^6.0.1",
21-
"@fastify/rate-limit": "^10.1.0",
22-
"@fastify/swagger": "^9.1.0",
21+
"@fastify/rate-limit": "^10.1.1",
22+
"@fastify/sensible": "^6.0.1",
23+
"@fastify/swagger": "^9.2.0",
2324
"@fastify/swagger-ui": "^5.0.1",
2425
"@fastify/under-pressure": "^9.0.1",
26+
"desm": "^1.3.1",
2527
"fastify": "^5.0.0",
2628
"fastify-plugin": "^5.0.1",
29+
"fluent-json-schema": "^5.0.0",
2730
"pg": "^8.13.0"
2831
},
2932
"scripts": {
30-
"dev": "export NODE_ENV=development && nodemon .",
33+
"dev": "nodemon .",
3134
"test": "tap --bail --timeout=5000 *.test.js",
3235
"lint": "biome check --write ."
3336
}

plugins/database.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import fp from "fastify-plugin";
22
import pg from "@fastify/postgres";
3-
43
import { config } from "dotenv";
54

65
config();
76

7+
// Set up the PostgreSQL client with your configuration
88
export default fp(async (app, opts) => {
99
await app.register(pg, {
10-
connectionString: process.env.POSTGRES_URL,
10+
connectionString: app.secrets.POSTGRES_URL,
1111
});
1212
})

plugins/jwt.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@ import jwt from "@fastify/jwt";
33

44
export default fp(async (app, opts) => {
55
await app.register(jwt, {
6-
secret: process.env.JWT_SECRET,
6+
secret: app.secrets.JWT_SECRET,
77
});
88
});

plugins/pressure.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import fp from "fastify-plugin";
2+
import underPressure from "@fastify/under-pressure";
3+
4+
export default fp(async (app, opts) => {
5+
await app.register(underPressure, {
6+
maxRequests: 100,
7+
maxRequestsPerMinute: 100,
8+
maxRequestsPerHour: 100,
9+
maxRequestsPerDay: 100,
10+
onRateLimit: (request, reply, options) => {
11+
return reply.code(429).send({
12+
message: "Too many requests",
13+
retryAfter: options.retryAfter,
14+
});
15+
}
16+
});
17+
})

plugins/sensible.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import fp from "fastify-plugin";
2+
import sensible from "@fastify/sensible";
3+
4+
export default fp(async (app, opts) => {
5+
await app.register(sensible);
6+
});

plugins/swagger.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ export default fp(async (app, opts) => {
2121
},
2222
});
2323
await app.register(swaggerUI, {
24-
routePrefix: process.env.NODE_ENV === "development" ? "/doc" : undefined,
24+
routePrefix: "/doc",
25+
exposeRoute: process.env.NODE_ENV !== "production",
2526
uiConfig: {
2627
docExpansion: "list",
2728
deepLinking: false,

routes/booksRoutes.js renamed to routes/books/routes.js

Lines changed: 9 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
import { getPaginatedData, paginationSchema } from "./utils.js";
1+
import { getPaginatedData, paginationSchema } from "../utils.js";
22

3-
export default async function registerBooksRoutes(app) {
3+
export default async function books(app) {
44
// GET all books
55
app.get(
66
"/books",
77
{
8-
schema: paginationSchema,
8+
schema: {
9+
querystring: paginationSchema,
10+
},
911
},
1012
async (request, reply) => {
1113
const { page, limit, search } = request.query;
1214
const client = await app.pg.connect();
13-
1415
try {
1516
const booksData = await client.query("SELECT * FROM books");
1617
const paginatedData = await getPaginatedData(
@@ -19,58 +20,19 @@ export default async function registerBooksRoutes(app) {
1920
page,
2021
limit,
2122
);
22-
return paginatedData
23+
return paginatedData;
2324
} catch (error) {
2425
reply.status(500).send(error);
25-
} finally {
26-
client.release();
27-
}
28-
},
29-
);
30-
31-
// GET a single book
32-
app.get("/books/:id",
33-
{
34-
schema: paginationSchema
35-
}
36-
,async (request, reply) => {
37-
const client = await app.pg.connect();
38-
const { id } = request.params;
39-
const { rows } = await client.query("SELECT * FROM books WHERE id = $1", [
40-
id,
41-
]);
42-
if (rows.length === 0) {
43-
reply.status(404).send("Book not found");
44-
} else {
45-
reply.send(rows[0]);
46-
}
47-
});
26+
}}
27+
)
4828

4929
// POST a new book
5030
app.post(
5131
"/books",
5232
{
5333
preValidation: app.authenticate,
5434
schema: {
55-
body: {
56-
type: "object",
57-
required: ["name", "url"],
58-
properties: {
59-
name: { type: "string" },
60-
url: { type: "string" },
61-
},
62-
},
63-
response: {
64-
200: {
65-
description: "Successful response",
66-
type: "object",
67-
properties: {
68-
id: { type: "integer" },
69-
name: { type: "string" },
70-
url: { type: "string" },
71-
},
72-
},
73-
},
35+
paginationSchema,
7436
},
7537
},
7638
async (request, reply) => {
@@ -126,7 +88,6 @@ export default async function registerBooksRoutes(app) {
12688
// DELETE a book
12789
app.delete(
12890
"/books/:id",
129-
13091
{
13192
preValidation: app.authenticate,
13293
},

routes/feeds/routes.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { getPaginatedData, paginationSchema } from "../utils.js";
2+
3+
export default async function feeds(app) {
4+
// GET all feeds
5+
app.get(
6+
"/feeds",
7+
{
8+
schema: {
9+
querystring: paginationSchema,
10+
},
11+
},
12+
async (request, reply) => {
13+
const { page, limit, search } = request.query;
14+
const client = await app.pg.connect();
15+
try {
16+
const feedsData = await client.query("SELECT * FROM feeds");
17+
const paginatedData = await getPaginatedData(
18+
feedsData.rows,
19+
search,
20+
page,
21+
limit,
22+
);
23+
return paginatedData;
24+
} catch (error) {
25+
reply.status(500).send(error);
26+
}}
27+
)
28+
}

routes/movies/routes.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { getPaginatedData, paginationSchema } from "../utils.js";
2+
3+
export default async function movies(app) {
4+
app.get(
5+
"/movies",
6+
{
7+
schema: {
8+
querystring: paginationSchema,
9+
},
10+
},
11+
async (request, reply) => {
12+
const { page, limit, search } = request.query;
13+
const client = await app.pg.connect();
14+
try {
15+
const feedsData = await client.query("SELECT * FROM movies");
16+
const paginatedData = await getPaginatedData(
17+
feedsData.rows,
18+
search,
19+
page,
20+
limit,
21+
);
22+
return paginatedData;
23+
} catch (error) {
24+
reply.status(500).send(error);
25+
}}
26+
)
27+
}

0 commit comments

Comments
 (0)