@appinit/cli 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/LICENSE +57 -22
  2. package/README.md +27 -95
  3. package/dist/index.d.ts +371 -2
  4. package/dist/index.js +3794 -95
  5. package/dist/templates/backend-express/.appinit-generate/project/.appinit/project.json +6 -0
  6. package/dist/templates/backend-express/.appinit-generate/project/.appinit/template.json +6 -0
  7. package/dist/templates/backend-express/.appinit-generate/project/.env.example +4 -0
  8. package/dist/templates/backend-express/.appinit-generate/project/.husky/pre-commit +1 -0
  9. package/dist/templates/backend-express/.appinit-generate/project/.prettierignore +3 -0
  10. package/dist/templates/backend-express/.appinit-generate/project/.prettierrc +28 -0
  11. package/dist/templates/backend-express/.appinit-generate/project/.vscode/settings.json +6 -0
  12. package/dist/templates/backend-express/.appinit-generate/project/Dockerfile +14 -0
  13. package/dist/templates/backend-express/.appinit-generate/project/README.md +26 -0
  14. package/dist/templates/backend-express/.appinit-generate/project/eslint.config.js +76 -0
  15. package/dist/templates/backend-express/.appinit-generate/project/nodemon.json +8 -0
  16. package/dist/templates/backend-express/.appinit-generate/project/package.json +69 -0
  17. package/dist/templates/backend-express/.appinit-generate/project/src/__tests__/health.test.ts +19 -0
  18. package/dist/templates/backend-express/.appinit-generate/project/src/__tests__/validation.test.ts +36 -0
  19. package/dist/templates/backend-express/.appinit-generate/project/src/app.ts +72 -0
  20. package/dist/templates/backend-express/.appinit-generate/project/src/config/defaults.ts +5 -0
  21. package/dist/templates/backend-express/.appinit-generate/project/src/config/dev.ts +3 -0
  22. package/dist/templates/backend-express/.appinit-generate/project/src/config/env.ts +62 -0
  23. package/dist/templates/backend-express/.appinit-generate/project/src/config/prod.ts +3 -0
  24. package/dist/templates/backend-express/.appinit-generate/project/src/config/test.ts +3 -0
  25. package/dist/templates/backend-express/.appinit-generate/project/src/errors/app-error.ts +20 -0
  26. package/dist/templates/backend-express/.appinit-generate/project/src/errors/not-found.ts +16 -0
  27. package/dist/templates/backend-express/.appinit-generate/project/src/middleware/error-handler.ts +73 -0
  28. package/dist/templates/backend-express/.appinit-generate/project/src/middleware/http-logger.ts +5 -0
  29. package/dist/templates/backend-express/.appinit-generate/project/src/middleware/validate.ts +17 -0
  30. package/dist/templates/backend-express/.appinit-generate/project/src/routes/health.ts +13 -0
  31. package/dist/templates/backend-express/.appinit-generate/project/src/routes/users.ts +12 -0
  32. package/dist/templates/backend-express/.appinit-generate/project/src/schema/create-user.schema.ts +8 -0
  33. package/dist/templates/backend-express/.appinit-generate/project/src/server.ts +54 -0
  34. package/dist/templates/backend-express/.appinit-generate/project/src/utils/logger.ts +21 -0
  35. package/dist/templates/backend-express/.appinit-generate/project/tsconfig.json +30 -0
  36. package/dist/templates/backend-express/.appinit-generate/project/vitest.config.ts +15 -0
  37. package/dist/templates/backend-express/.appinit-preview/project/.appinit/project.json +6 -0
  38. package/dist/templates/backend-express/.appinit-preview/project/.appinit/template.json +6 -0
  39. package/dist/templates/backend-express/.appinit-preview/project/.env.example +4 -0
  40. package/dist/templates/backend-express/.appinit-preview/project/.husky/pre-commit +1 -0
  41. package/dist/templates/backend-express/.appinit-preview/project/.prettierignore +3 -0
  42. package/dist/templates/backend-express/.appinit-preview/project/.prettierrc +28 -0
  43. package/dist/templates/backend-express/.appinit-preview/project/.vscode/settings.json +6 -0
  44. package/dist/templates/backend-express/.appinit-preview/project/Dockerfile +14 -0
  45. package/dist/templates/backend-express/.appinit-preview/project/README.md +26 -0
  46. package/dist/templates/backend-express/.appinit-preview/project/eslint.config.js +76 -0
  47. package/dist/templates/backend-express/.appinit-preview/project/nodemon.json +8 -0
  48. package/dist/templates/backend-express/.appinit-preview/project/package.json +69 -0
  49. package/dist/templates/backend-express/.appinit-preview/project/src/__tests__/health.test.ts +19 -0
  50. package/dist/templates/backend-express/.appinit-preview/project/src/__tests__/validation.test.ts +36 -0
  51. package/dist/templates/backend-express/.appinit-preview/project/src/app.ts +72 -0
  52. package/dist/templates/backend-express/.appinit-preview/project/src/config/defaults.ts +5 -0
  53. package/dist/templates/backend-express/.appinit-preview/project/src/config/dev.ts +3 -0
  54. package/dist/templates/backend-express/.appinit-preview/project/src/config/env.ts +62 -0
  55. package/dist/templates/backend-express/.appinit-preview/project/src/config/prod.ts +3 -0
  56. package/dist/templates/backend-express/.appinit-preview/project/src/config/test.ts +3 -0
  57. package/dist/templates/backend-express/.appinit-preview/project/src/errors/app-error.ts +20 -0
  58. package/dist/templates/backend-express/.appinit-preview/project/src/errors/not-found.ts +16 -0
  59. package/dist/templates/backend-express/.appinit-preview/project/src/middleware/error-handler.ts +73 -0
  60. package/dist/templates/backend-express/.appinit-preview/project/src/middleware/http-logger.ts +5 -0
  61. package/dist/templates/backend-express/.appinit-preview/project/src/middleware/validate.ts +17 -0
  62. package/dist/templates/backend-express/.appinit-preview/project/src/routes/health.ts +13 -0
  63. package/dist/templates/backend-express/.appinit-preview/project/src/routes/users.ts +12 -0
  64. package/dist/templates/backend-express/.appinit-preview/project/src/schema/create-user.schema.ts +8 -0
  65. package/dist/templates/backend-express/.appinit-preview/project/src/server.ts +54 -0
  66. package/dist/templates/backend-express/.appinit-preview/project/src/utils/logger.ts +21 -0
  67. package/dist/templates/backend-express/.appinit-preview/project/tsconfig.json +30 -0
  68. package/dist/templates/backend-express/.appinit-preview/project/vitest.config.ts +15 -0
  69. package/dist/templates/backend-express/README.md +107 -0
  70. package/dist/templates/backend-express/appinit.template.json +25 -0
  71. package/dist/templates/backend-express/docs/README.md +11 -0
  72. package/dist/templates/backend-express/package.json +5 -0
  73. package/dist/templates/backend-express/template/.env.example +4 -0
  74. package/dist/templates/backend-express/template/.husky/pre-commit +1 -0
  75. package/dist/templates/backend-express/template/.prettierignore +3 -0
  76. package/dist/templates/backend-express/template/.prettierrc +28 -0
  77. package/dist/templates/backend-express/template/.vscode/settings.json +6 -0
  78. package/dist/templates/backend-express/template/Dockerfile +14 -0
  79. package/dist/templates/backend-express/template/README.md__tmpl +26 -0
  80. package/dist/templates/backend-express/template/eslint.config.js +76 -0
  81. package/dist/templates/backend-express/template/nodemon.json +8 -0
  82. package/dist/templates/backend-express/template/package.json__tmpl +69 -0
  83. package/dist/templates/backend-express/template/src/__tests__/health.test.ts +19 -0
  84. package/dist/templates/backend-express/template/src/__tests__/validation.test.ts +36 -0
  85. package/dist/templates/backend-express/template/src/app.ts +72 -0
  86. package/dist/templates/backend-express/template/src/config/defaults.ts +5 -0
  87. package/dist/templates/backend-express/template/src/config/dev.ts +3 -0
  88. package/dist/templates/backend-express/template/src/config/env.ts +62 -0
  89. package/dist/templates/backend-express/template/src/config/prod.ts +3 -0
  90. package/dist/templates/backend-express/template/src/config/test.ts +3 -0
  91. package/dist/templates/backend-express/template/src/errors/app-error.ts +20 -0
  92. package/dist/templates/backend-express/template/src/errors/not-found.ts +16 -0
  93. package/dist/templates/backend-express/template/src/middleware/error-handler.ts +73 -0
  94. package/dist/templates/backend-express/template/src/middleware/http-logger.ts +5 -0
  95. package/dist/templates/backend-express/template/src/middleware/validate.ts +17 -0
  96. package/dist/templates/backend-express/template/src/routes/health.ts +13 -0
  97. package/dist/templates/backend-express/template/src/routes/users.ts +12 -0
  98. package/dist/templates/backend-express/template/src/schema/create-user.schema.ts +8 -0
  99. package/dist/templates/backend-express/template/src/server.ts +54 -0
  100. package/dist/templates/backend-express/template/src/utils/logger.ts +21 -0
  101. package/dist/templates/backend-express/template/tsconfig.json +30 -0
  102. package/dist/templates/backend-express/template/vitest.config.ts +15 -0
  103. package/dist/templates/backend-express/template.config.ts +36 -0
  104. package/dist/templates/backend-express/variables/defaults.json +7 -0
  105. package/dist/templates/backend-express/variables/defaults.ts +4 -0
  106. package/dist/templates/backend-express/variables/schema.json +22 -0
  107. package/dist/templates/backend-express/variables/schema.ts +11 -0
  108. package/dist/templates/backend-express/variables/transform.ts +6 -0
  109. package/dist/templates/backend-fastify/.appinit-generate/project/.appinit/project.json +6 -0
  110. package/dist/templates/backend-fastify/.appinit-generate/project/.appinit/template.json +6 -0
  111. package/dist/templates/backend-fastify/.appinit-generate/project/README.md +26 -0
  112. package/dist/templates/backend-fastify/.appinit-generate/project/nodemon.json +8 -0
  113. package/dist/templates/backend-fastify/.appinit-generate/project/package-lock.json +3245 -0
  114. package/dist/templates/backend-fastify/.appinit-generate/project/package.json +24 -0
  115. package/dist/templates/backend-fastify/.appinit-generate/project/src/app.ts +13 -0
  116. package/dist/templates/backend-fastify/.appinit-generate/project/src/server.ts +15 -0
  117. package/dist/templates/backend-fastify/.appinit-generate/project/tsconfig.json +30 -0
  118. package/dist/templates/backend-fastify/README.md +27 -0
  119. package/dist/templates/backend-fastify/appinit.template.json +25 -0
  120. package/dist/templates/backend-fastify/docs/README.md +12 -0
  121. package/dist/templates/backend-fastify/package.json +5 -0
  122. package/dist/templates/backend-fastify/template/README.md__tmpl +26 -0
  123. package/dist/templates/backend-fastify/template/nodemon.json +8 -0
  124. package/dist/templates/backend-fastify/template/package.json__tmpl +24 -0
  125. package/dist/templates/backend-fastify/template/src/app.ts +13 -0
  126. package/dist/templates/backend-fastify/template/src/server.ts +15 -0
  127. package/dist/templates/backend-fastify/template/tsconfig.json +30 -0
  128. package/dist/templates/backend-fastify/template.config.ts +32 -0
  129. package/dist/templates/backend-fastify/variables/defaults.json +4 -0
  130. package/dist/templates/backend-fastify/variables/defaults.ts +3 -0
  131. package/dist/templates/backend-fastify/variables/schema.json +7 -0
  132. package/dist/templates/backend-fastify/variables/schema.ts +1 -0
  133. package/dist/templates/backend-fastify/variables/transform.ts +3 -0
  134. package/dist/templates/frontend-nextjs/.appinit-generate/project/.appinit/project.json +6 -0
  135. package/dist/templates/frontend-nextjs/.appinit-generate/project/.appinit/template.json +6 -0
  136. package/dist/templates/frontend-nextjs/.appinit-generate/project/README.md +33 -0
  137. package/dist/templates/frontend-nextjs/.appinit-generate/project/app/favicon.ico +0 -0
  138. package/dist/templates/frontend-nextjs/.appinit-generate/project/app/globals.css +42 -0
  139. package/dist/templates/frontend-nextjs/.appinit-generate/project/app/layout.tsx +33 -0
  140. package/dist/templates/frontend-nextjs/.appinit-generate/project/app/page.module.css +141 -0
  141. package/dist/templates/frontend-nextjs/.appinit-generate/project/app/page.tsx +67 -0
  142. package/dist/templates/frontend-nextjs/.appinit-generate/project/next-env.d.ts +6 -0
  143. package/dist/templates/frontend-nextjs/.appinit-generate/project/next.config.ts +8 -0
  144. package/dist/templates/frontend-nextjs/.appinit-generate/project/package.json +25 -0
  145. package/dist/templates/frontend-nextjs/.appinit-generate/project/public/file.svg +1 -0
  146. package/dist/templates/frontend-nextjs/.appinit-generate/project/public/globe.svg +1 -0
  147. package/dist/templates/frontend-nextjs/.appinit-generate/project/public/next.svg +1 -0
  148. package/dist/templates/frontend-nextjs/.appinit-generate/project/public/vercel.svg +1 -0
  149. package/dist/templates/frontend-nextjs/.appinit-generate/project/public/window.svg +1 -0
  150. package/dist/templates/frontend-nextjs/.appinit-generate/project/tsconfig.json +34 -0
  151. package/dist/templates/frontend-nextjs/.appinit-preview/project/.appinit/project.json +6 -0
  152. package/dist/templates/frontend-nextjs/.appinit-preview/project/.appinit/template.json +6 -0
  153. package/dist/templates/frontend-nextjs/.appinit-preview/project/README.md +33 -0
  154. package/dist/templates/frontend-nextjs/.appinit-preview/project/app/favicon.ico +0 -0
  155. package/dist/templates/frontend-nextjs/.appinit-preview/project/app/globals.css +42 -0
  156. package/dist/templates/frontend-nextjs/.appinit-preview/project/app/layout.tsx +33 -0
  157. package/dist/templates/frontend-nextjs/.appinit-preview/project/app/page.module.css +141 -0
  158. package/dist/templates/frontend-nextjs/.appinit-preview/project/app/page.tsx +67 -0
  159. package/dist/templates/frontend-nextjs/.appinit-preview/project/next-env.d.ts +6 -0
  160. package/dist/templates/frontend-nextjs/.appinit-preview/project/next.config.ts +8 -0
  161. package/dist/templates/frontend-nextjs/.appinit-preview/project/package.json +25 -0
  162. package/dist/templates/frontend-nextjs/.appinit-preview/project/public/file.svg +1 -0
  163. package/dist/templates/frontend-nextjs/.appinit-preview/project/public/globe.svg +1 -0
  164. package/dist/templates/frontend-nextjs/.appinit-preview/project/public/next.svg +1 -0
  165. package/dist/templates/frontend-nextjs/.appinit-preview/project/public/vercel.svg +1 -0
  166. package/dist/templates/frontend-nextjs/.appinit-preview/project/public/window.svg +1 -0
  167. package/dist/templates/frontend-nextjs/.appinit-preview/project/tsconfig.json +34 -0
  168. package/dist/templates/frontend-nextjs/README.md +27 -0
  169. package/dist/templates/frontend-nextjs/appinit.template.json +25 -0
  170. package/dist/templates/frontend-nextjs/docs/README.md +12 -0
  171. package/dist/templates/frontend-nextjs/package.json +5 -0
  172. package/dist/templates/frontend-nextjs/template/README.md__tmpl +33 -0
  173. package/dist/templates/frontend-nextjs/template/app/favicon.ico +0 -0
  174. package/dist/templates/frontend-nextjs/template/app/globals.css +42 -0
  175. package/dist/templates/frontend-nextjs/template/app/layout.tsx +33 -0
  176. package/dist/templates/frontend-nextjs/template/app/page.module.css +141 -0
  177. package/dist/templates/frontend-nextjs/template/app/page.tsx +67 -0
  178. package/dist/templates/frontend-nextjs/template/next-env.d.ts +6 -0
  179. package/dist/templates/frontend-nextjs/template/next.config.ts +8 -0
  180. package/dist/templates/frontend-nextjs/template/package.json__tmpl +25 -0
  181. package/dist/templates/frontend-nextjs/template/public/file.svg +1 -0
  182. package/dist/templates/frontend-nextjs/template/public/globe.svg +1 -0
  183. package/dist/templates/frontend-nextjs/template/public/next.svg +1 -0
  184. package/dist/templates/frontend-nextjs/template/public/vercel.svg +1 -0
  185. package/dist/templates/frontend-nextjs/template/public/window.svg +1 -0
  186. package/dist/templates/frontend-nextjs/template/tsconfig.json +34 -0
  187. package/dist/templates/frontend-nextjs/template.config.ts +28 -0
  188. package/dist/templates/frontend-nextjs/variables/defaults.json +6 -0
  189. package/dist/templates/frontend-nextjs/variables/defaults.ts +4 -0
  190. package/dist/templates/frontend-nextjs/variables/schema.json +22 -0
  191. package/dist/templates/frontend-nextjs/variables/schema.ts +7 -0
  192. package/dist/templates/frontend-nextjs/variables/transform.ts +3 -0
  193. package/dist/templates/frontend-react/.appinit-generate/project/.appinit/project.json +6 -0
  194. package/dist/templates/frontend-react/.appinit-generate/project/.appinit/template.json +6 -0
  195. package/dist/templates/frontend-react/appinit.template.json +26 -0
  196. package/dist/templates/frontend-react/template.config.ts +14 -18
  197. package/dist/templates/frontend-react/template.meta.json +10 -0
  198. package/dist/templates/frontend-react/variables/defaults.json +6 -0
  199. package/dist/templates/frontend-react/variables/schema.json +15 -0
  200. package/dist/templates/frontend-vue/README.md +27 -0
  201. package/dist/templates/frontend-vue/appinit.template.json +25 -0
  202. package/dist/templates/frontend-vue/docs/README.md +12 -0
  203. package/dist/templates/frontend-vue/package.json +5 -0
  204. package/dist/templates/frontend-vue/template/README.md__tmpl +31 -0
  205. package/dist/templates/frontend-vue/template/index.html +13 -0
  206. package/dist/templates/frontend-vue/template/package.json__tmpl +25 -0
  207. package/dist/templates/frontend-vue/template/public/vite.svg +1 -0
  208. package/dist/templates/frontend-vue/template/src/App.vue +30 -0
  209. package/dist/templates/frontend-vue/template/src/assets/vue.svg +1 -0
  210. package/dist/templates/frontend-vue/template/src/components/HelloWorld.vue +42 -0
  211. package/dist/templates/frontend-vue/template/src/main.ts +6 -0
  212. package/dist/templates/frontend-vue/template/src/style.css +79 -0
  213. package/dist/templates/frontend-vue/template/tsconfig.app.json +16 -0
  214. package/dist/templates/frontend-vue/template/tsconfig.json +7 -0
  215. package/dist/templates/frontend-vue/template/tsconfig.node.json +26 -0
  216. package/dist/templates/frontend-vue/template/vite.config.ts +7 -0
  217. package/dist/templates/frontend-vue/template.config.ts +32 -0
  218. package/dist/templates/frontend-vue/variables/defaults.json +6 -0
  219. package/dist/templates/frontend-vue/variables/defaults.ts +3 -0
  220. package/dist/templates/frontend-vue/variables/schema.json +22 -0
  221. package/dist/templates/frontend-vue/variables/schema.ts +1 -0
  222. package/dist/templates/frontend-vue/variables/transform.ts +3 -0
  223. package/package.json +41 -51
  224. package/bin/appinit.js +0 -15
  225. /package/dist/templates/frontend-react/template/{.env__tmpl → .env.example__tmpl} +0 -0
@@ -0,0 +1,19 @@
1
+ import request from "supertest";
2
+
3
+ import { app } from "../app.js";
4
+
5
+ describe("Health endpoints", () => {
6
+ it("GET /health should return ok", async () => {
7
+ const res = await request(app).get("/api/health");
8
+
9
+ expect(res.status).toBe(200);
10
+ expect(res.body).toEqual({ status: "ok" });
11
+ });
12
+
13
+ it("GET /ready should return ready", async () => {
14
+ const res = await request(app).get("/api/health/ready");
15
+
16
+ expect(res.status).toBe(200);
17
+ expect(res.body).toEqual({ ready: true });
18
+ });
19
+ });
@@ -0,0 +1,36 @@
1
+ import request from "supertest";
2
+
3
+ import { app } from "../app.js";
4
+
5
+ describe("Zod validation errors", () => {
6
+ it("returns 400 with field errors", async () => {
7
+ const res = await request(app)
8
+ .post("/api/users")
9
+ .send({ email: "invalid", password: "123" });
10
+
11
+ expect(res.status).toBe(400);
12
+ expect(res.body).toEqual({
13
+ error: {
14
+ code: "VALIDATION_ERROR",
15
+ message: "Invalid request",
16
+ fields: {
17
+ "body.email": ["Invalid email address"],
18
+ "body.password": [
19
+ "Too small: expected string to have >=8 characters",
20
+ ],
21
+ },
22
+ },
23
+ });
24
+ });
25
+
26
+ it("returns 201 with register", async () => {
27
+ const res = await request(app)
28
+ .post("/api/users")
29
+ .send({ email: "abc@gmail.com", password: "12345678" });
30
+
31
+ expect(res.status).toBe(201);
32
+ expect(res.body).toEqual({
33
+ ok: true,
34
+ });
35
+ });
36
+ });
@@ -0,0 +1,72 @@
1
+ import compression from "compression";
2
+ import cors from "cors";
3
+ import express, { Express } from "express";
4
+ import type { Request, Response } from "express";
5
+ import helmet from "helmet";
6
+ import morgan from "morgan";
7
+ import path from "path";
8
+ import { fileURLToPath } from "url";
9
+
10
+ import { env } from "./config/env";
11
+ import { notFoundHandler } from "./errors/not-found";
12
+ import { errorHandler } from "./middleware/error-handler";
13
+ import { httpLogger } from "./middleware/http-logger";
14
+ import { healthRouter } from "./routes/health";
15
+ import { userRouter } from "./routes/users";
16
+
17
+ // Define __dirname for ESM
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = path.dirname(__filename);
20
+
21
+ const app: Express = express();
22
+
23
+ /**
24
+ * 1. SECURITY MIDDLEWARE
25
+ */
26
+ app.use(helmet());
27
+ app.use(cors({ origin: env.corsOrigin }));
28
+
29
+ /**
30
+ * 2. PERFORMANCE MIDDLEWARE
31
+ */
32
+ app.use(compression());
33
+
34
+ /**
35
+ * 3. REQUEST PARSING
36
+ */
37
+ app.use(express.json({ limit: "10kb" }));
38
+ app.use(express.urlencoded({ extended: true, limit: "10kb" }));
39
+
40
+ /**
41
+ * 4. LOGGING & STATIC FILES
42
+ */
43
+ app.use(httpLogger);
44
+ if (process.env.NODE_ENV !== "production") {
45
+ app.use(morgan("dev"));
46
+ } else {
47
+ app.use(morgan("combined"));
48
+ }
49
+
50
+ // Fixed static path for ESM
51
+ app.use(express.static(path.join(__dirname, "../public")));
52
+
53
+ /**
54
+ * 5. SETTINGS
55
+ */
56
+ app.disable("x-powered-by");
57
+
58
+ // route
59
+ app.get("/", (_req: Request, res: Response) => {
60
+ res.status(200).json({
61
+ version: "1.0.0",
62
+ });
63
+ });
64
+
65
+ app.use("/api/health", healthRouter);
66
+ app.use("/api/users", userRouter);
67
+ /* 404 */
68
+ app.use(notFoundHandler);
69
+
70
+ /* error handler (LAST) */
71
+ app.use(errorHandler);
72
+ export { app };
@@ -0,0 +1,5 @@
1
+ export const defaults = {
2
+ port: 3001,
3
+ logLevel: "info" as const,
4
+ corsOrigin: "*",
5
+ };
@@ -0,0 +1,3 @@
1
+ export const developmentConfig = {
2
+ logLevel: "debug" as const,
3
+ };
@@ -0,0 +1,62 @@
1
+ import { z } from "zod";
2
+
3
+ import { defaults } from "./defaults";
4
+ import { developmentConfig } from "./dev";
5
+ import { productionConfig } from "./prod";
6
+ import { testConfig } from "./test";
7
+
8
+ /**
9
+ * Load dotenv ONLY outside production
10
+ */
11
+ if (process.env.NODE_ENV !== "production") {
12
+ await import("dotenv/config");
13
+ }
14
+
15
+ /**
16
+ * Raw env schema (what the platform provides)
17
+ */
18
+ const rawEnvSchema = z.object({
19
+ NODE_ENV: z
20
+ .enum(["development", "test", "production"])
21
+ .default("development"),
22
+ PORT: z.coerce.number().int().positive().optional(),
23
+ LOG_LEVEL: z
24
+ .enum(["fatal", "error", "warn", "info", "debug", "trace"])
25
+ .optional(),
26
+ CORS_ORIGIN: z.string().optional(),
27
+ });
28
+
29
+ /**
30
+ * Validate process.env
31
+ */
32
+ const parsed = rawEnvSchema.safeParse(process.env);
33
+
34
+ if (!parsed.success) {
35
+ console.error("❌ Invalid environment variables");
36
+ console.error(parsed.error.format());
37
+ process.exit(1);
38
+ }
39
+
40
+ const nodeEnv = parsed.data.NODE_ENV;
41
+
42
+ /**
43
+ * Select environment config
44
+ */
45
+ const envConfigMap = {
46
+ development: developmentConfig,
47
+ test: testConfig,
48
+ production: productionConfig,
49
+ } as const;
50
+
51
+ const envOverrides = envConfigMap[nodeEnv];
52
+
53
+ /**
54
+ * Final resolved config (single export)
55
+ */
56
+ export const env = {
57
+ nodeEnv,
58
+
59
+ port: parsed.data.PORT ?? defaults.port,
60
+ logLevel: parsed.data.LOG_LEVEL ?? envOverrides.logLevel ?? defaults.logLevel,
61
+ corsOrigin: parsed.data.CORS_ORIGIN ?? defaults.corsOrigin,
62
+ } as const;
@@ -0,0 +1,3 @@
1
+ export const productionConfig = {
2
+ logLevel: "info" as const,
3
+ };
@@ -0,0 +1,3 @@
1
+ export const testConfig = {
2
+ logLevel: "silent" as const,
3
+ };
@@ -0,0 +1,20 @@
1
+ export class AppError extends Error {
2
+ public readonly statusCode: number;
3
+ public readonly code: string;
4
+ public readonly isOperational: boolean;
5
+
6
+ constructor(
7
+ message: string,
8
+ options: {
9
+ statusCode?: number;
10
+ code?: string;
11
+ isOperational?: boolean;
12
+ } = {},
13
+ ) {
14
+ super(message);
15
+
16
+ this.statusCode = options.statusCode ?? 500;
17
+ this.code = options.code ?? "INTERNAL_SERVER_ERROR";
18
+ this.isOperational = options.isOperational ?? true;
19
+ }
20
+ }
@@ -0,0 +1,16 @@
1
+ import type { NextFunction, Request, Response } from "express";
2
+
3
+ import { AppError } from "../errors/app-error.js";
4
+
5
+ export function notFoundHandler(
6
+ _req: Request,
7
+ _res: Response,
8
+ next: NextFunction,
9
+ ) {
10
+ next(
11
+ new AppError("Route not found", {
12
+ statusCode: 404,
13
+ code: "NOT_FOUND",
14
+ }),
15
+ );
16
+ }
@@ -0,0 +1,73 @@
1
+ import type { NextFunction, Request, Response } from "express";
2
+ import { ZodError } from "zod";
3
+
4
+ import { env } from "../config/env.js";
5
+ import { AppError } from "../errors/app-error.js";
6
+ import { logger } from "../utils/logger.js";
7
+
8
+ export function errorHandler(
9
+ err: unknown,
10
+ _req: Request,
11
+ res: Response,
12
+ _next: NextFunction,
13
+ ) {
14
+ /**
15
+ * Zod validation errors (400)
16
+ */
17
+ if (err instanceof ZodError) {
18
+ const fieldErrors: Record<string, string[]> = {};
19
+
20
+ for (const issue of err.issues) {
21
+ const field = issue.path.join(".") || "root";
22
+ fieldErrors[field] ??= [];
23
+ fieldErrors[field].push(issue.message);
24
+ }
25
+
26
+ logger.warn({ fieldErrors }, "Request validation failed");
27
+
28
+ return res.status(400).json({
29
+ error: {
30
+ code: "VALIDATION_ERROR",
31
+ message: "Invalid request",
32
+ fields: fieldErrors,
33
+ },
34
+ });
35
+ }
36
+ // Normalize error
37
+ const error =
38
+ err instanceof AppError
39
+ ? err
40
+ : new AppError("Internal server error", {
41
+ statusCode: 500,
42
+ code: "INTERNAL_SERVER_ERROR",
43
+ isOperational: false,
44
+ });
45
+
46
+ // Log full error (always)
47
+ logger.error(
48
+ {
49
+ err,
50
+ statusCode: error.statusCode,
51
+ code: error.code,
52
+ },
53
+ "Unhandled application error",
54
+ );
55
+
56
+ // Response (safe for clients)
57
+ const response: Record<string, unknown> = {
58
+ error: {
59
+ code: error.code,
60
+ message: error.message,
61
+ },
62
+ };
63
+
64
+ // Dev-only details
65
+ if (env.nodeEnv !== "production") {
66
+ response.error = {
67
+ ...(response.error ?? {}),
68
+ details: err instanceof Error ? err.stack : err,
69
+ };
70
+ }
71
+
72
+ res.status(error.statusCode).json(response);
73
+ }
@@ -0,0 +1,5 @@
1
+ import pinoHttp from "pino-http";
2
+
3
+ import { logger } from "../utils/logger.js";
4
+
5
+ export const httpLogger = pinoHttp({ logger });
@@ -0,0 +1,17 @@
1
+ import type { NextFunction, Request, Response } from "express";
2
+ import type { ZodSchema } from "zod";
3
+
4
+ export const validate =
5
+ <T>(schema: ZodSchema<T>) =>
6
+ (req: Request, _res: Response, next: NextFunction) => {
7
+ try {
8
+ schema.parse({
9
+ body: req.body,
10
+ query: req.query,
11
+ params: req.params,
12
+ });
13
+ next();
14
+ } catch (err) {
15
+ next(err);
16
+ }
17
+ };
@@ -0,0 +1,13 @@
1
+ import { Router } from "express";
2
+
3
+ export const router = Router();
4
+
5
+ router.get("/", (_req, res) => {
6
+ res.status(200).json({ status: "ok" });
7
+ });
8
+
9
+ router.get("/ready", (_req, res) => {
10
+ res.status(200).json({ ready: true });
11
+ });
12
+
13
+ export { router as healthRouter };
@@ -0,0 +1,12 @@
1
+ import { Router } from "express";
2
+
3
+ import { validate } from "../middleware/validate";
4
+ import { createUserSchema } from "../schema/create-user.schema";
5
+
6
+ const router = Router();
7
+
8
+ router.post("/", validate(createUserSchema), (_req, res) => {
9
+ res.status(201).json({ ok: true });
10
+ });
11
+
12
+ export { router as userRouter };
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+
3
+ export const createUserSchema = z.object({
4
+ body: z.object({
5
+ email: z.email(),
6
+ password: z.string().min(8),
7
+ }),
8
+ });
@@ -0,0 +1,54 @@
1
+ import { app } from "./app";
2
+ import { logger } from "./utils/logger";
3
+
4
+ // Assuming the Pino logger from before
5
+
6
+ const PORT = process.env.PORT || 3001;
7
+
8
+ const server = app.listen(PORT, () => {
9
+ logger.info(`🚀 Server version 1.0.0 started`);
10
+ logger.info(`📡 Listening on http://localhost:${PORT}`);
11
+ logger.info(`🌍 Environment: ${process.env.NODE_ENV || "development"}`);
12
+ });
13
+
14
+ /**
15
+ * GRACEFUL SHUTDOWN LOGIC
16
+ * Ensures the server closes existing connections before exiting.
17
+ */
18
+ const shutdown = (signal: string) => {
19
+ logger.info(`${signal} received. Starting graceful shutdown...`);
20
+
21
+ server.close(() => {
22
+ logger.info("⛔ HTTP server closed.");
23
+
24
+ // Close database connections here (e.g., prisma.$disconnect() or mongoose.connection.close())
25
+
26
+ process.exit(0);
27
+ });
28
+
29
+ // Force close after 10 seconds if graceful shutdown fails
30
+ setTimeout(() => {
31
+ logger.error(
32
+ "Could not close connections in time, forcefully shutting down",
33
+ );
34
+ process.exit(1);
35
+ }, 10000);
36
+ };
37
+
38
+ // Listen for termination signals
39
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
40
+ process.on("SIGINT", () => shutdown("SIGINT"));
41
+
42
+ /**
43
+ * UNHANDLED REJECTION/EXCEPTION CATCHING
44
+ * Prevents the process from crashing silently.
45
+ */
46
+ process.on("unhandledRejection", (reason, promise) => {
47
+ logger.fatal({ promise, reason }, "Unhandled Rejection at Promise");
48
+ throw reason; // Let uncaughtException handle it
49
+ });
50
+
51
+ process.on("uncaughtException", (error) => {
52
+ logger.fatal(error, "Uncaught Exception thrown");
53
+ process.exit(1);
54
+ });
@@ -0,0 +1,21 @@
1
+ import pino from "pino";
2
+
3
+ import { env } from "../config/env.js";
4
+
5
+ const isProduction = process.env.NODE_ENV === "production";
6
+
7
+ export const logger = pino({
8
+ level: env.logLevel,
9
+ // Redact sensitive data from logs for security
10
+ redact: ["req.headers.authorization", "password", "confirmPassword"],
11
+ transport: isProduction
12
+ ? undefined // In production, output raw JSON for Datadog/ELK
13
+ : {
14
+ target: "pino-pretty",
15
+ options: {
16
+ colorize: true,
17
+ translateTime: "SYS:standard",
18
+ ignore: "pid,hostname",
19
+ },
20
+ },
21
+ });
@@ -0,0 +1,30 @@
1
+ {
2
+ "compilerOptions": {
3
+ /* Language & runtime */
4
+ "target": "ES2022",
5
+ "module": "ESNext",
6
+ "moduleResolution": "node",
7
+
8
+ /* Project structure */
9
+ "rootDir": "src",
10
+ "outDir": "dist",
11
+
12
+ /* Interop & correctness */
13
+ "esModuleInterop": true,
14
+ "allowSyntheticDefaultImports": true,
15
+
16
+ /* Safety */
17
+ "strict": true,
18
+ "forceConsistentCasingInFileNames": true,
19
+ "skipLibCheck": true,
20
+ "noEmitOnError": true,
21
+
22
+ /* DX & production debugging */
23
+ "sourceMap": true,
24
+
25
+ /* Node environment */
26
+ "types": ["node", "vitest/globals"]
27
+ },
28
+ "include": ["src"],
29
+ "exclude": ["node_modules", "dist", "__tests__"]
30
+ }
@@ -0,0 +1,15 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ environment: "node",
6
+ globals: true,
7
+ clearMocks: true,
8
+ restoreMocks: true,
9
+ coverage: {
10
+ provider: "v8",
11
+ reporter: ["text", "html"],
12
+ exclude: ["dist", "node_modules"],
13
+ },
14
+ },
15
+ });
@@ -0,0 +1,36 @@
1
+ export const config = {
2
+ // Canonical template identity
3
+ id: "backend.backend-express.basic",
4
+ version: "0.1.0",
5
+
6
+ // ATS contract
7
+ appinitSpec: "1.0",
8
+ atsVersion: "1.0",
9
+
10
+ // Classification
11
+ category: "backend",
12
+ language: "ts",
13
+ // Rendering
14
+ engine: "none", // liquid | none, default is none
15
+
16
+ // Output
17
+ output: {
18
+ dir: ".",
19
+ },
20
+
21
+ // Metadata
22
+ name: "backend-express",
23
+ description: "Backend template (Express, Fastify, etc.)",
24
+
25
+ tags: ["backend", "api", "server"],
26
+
27
+ license: "MIT",
28
+ postInstall: [],
29
+ author: {
30
+ name: "AppInit",
31
+ },
32
+
33
+ paths: {
34
+ template: "./template",
35
+ },
36
+ };
@@ -0,0 +1,7 @@
1
+ {
2
+ "projectName": "my-express-app",
3
+ "description": "Backend API built with AppInit",
4
+ "author": "",
5
+ "licenseType": "MIT",
6
+ "port": 3001
7
+ }
@@ -0,0 +1,4 @@
1
+ export const defaults = {
2
+ projectName: "my-express-app",
3
+ port: 3001,
4
+ };
@@ -0,0 +1,22 @@
1
+ {
2
+ "projectName": {
3
+ "type": "string",
4
+ "required": true,
5
+ "minLength": 1
6
+ },
7
+ "description": {
8
+ "type": "string"
9
+ },
10
+ "author": {
11
+ "type": "string"
12
+ },
13
+ "licenseType": {
14
+ "type": "string",
15
+ "enum": [
16
+ "MIT",
17
+ "Apache-2.0",
18
+ "ISC",
19
+ "UNLICENSED"
20
+ ]
21
+ }
22
+ }
@@ -0,0 +1,11 @@
1
+ export const schema = {
2
+ projectName: {
3
+ type: "string",
4
+ required: true,
5
+ message: "Project name",
6
+ },
7
+ port: {
8
+ type: "number",
9
+ default: 3001,
10
+ },
11
+ };
@@ -0,0 +1,6 @@
1
+ export function transform(vars: any) {
2
+ return {
3
+ ...vars,
4
+ safeName: vars.projectName.replace(/\s+/g, "-").toLowerCase(),
5
+ };
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "project",
3
+ "createdAt": "2025-12-18T14:47:09.961Z",
4
+ "cliVersion": "unknown",
5
+ "ats": "1.0"
6
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "backend.fastify.basic",
3
+ "version": "1.0.0",
4
+ "source": "builtin",
5
+ "installedAt": "2025-12-18T14:47:09.963Z"
6
+ }
@@ -0,0 +1,26 @@
1
+ # ⚛️ backend-fastify
2
+
3
+ This project was generated using **AppInit 1.0**.
4
+
5
+ ## 🚀 Quick Start
6
+
7
+ ```bash
8
+ pnpm install
9
+ pnpm dev
10
+ ```
11
+
12
+ ## 🛠️ Stack Configuration
13
+
14
+ - **Framework**: React 18+ (Vite)
15
+ - **Engine Used**:
16
+ - **Generated At**:
17
+
18
+ ## 📂 Project Structure
19
+
20
+ - `src/`: Your React components and logic.
21
+ - `public/`: Static assets (images, icons).
22
+ - `tailwind.config.js`: Styling configuration.
23
+
24
+ ---
25
+
26
+ _Built with speed using the AppInit Scaffolding Engine._
@@ -0,0 +1,8 @@
1
+ {
2
+ "watch": ["src"],
3
+ "ext": "ts",
4
+ "ignore": ["dist", "node_modules"],
5
+ "exec": "tsx src/server.ts",
6
+ "signal": "SIGTERM",
7
+ "delay": "100"
8
+ }