create-rex-app 1.0.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 (53) hide show
  1. package/index.js +148 -0
  2. package/package.json +31 -0
  3. package/template/README.md +71 -0
  4. package/template/_gitignore +25 -0
  5. package/template/api/routes.js +9 -0
  6. package/template/eslint.config.js +29 -0
  7. package/template/index.html +13 -0
  8. package/template/package-lock.json +4667 -0
  9. package/template/package.json +33 -0
  10. package/template/public/rex logo.png +0 -0
  11. package/template/public/rex.png +0 -0
  12. package/template/server.js +77 -0
  13. package/template/src/App.css +42 -0
  14. package/template/src/App.jsx +326 -0
  15. package/template/src/assets/react.svg +1 -0
  16. package/template/src/hooks/useTest.js +18 -0
  17. package/template/src/index.css +1 -0
  18. package/template/src/main.jsx +14 -0
  19. package/template/vercel.json +8 -0
  20. package/template/vite.config.js +7 -0
  21. package/template-mongo/README.md +96 -0
  22. package/template-mongo/api/db.js +17 -0
  23. package/template-mongo/api/middleware/authMiddleware.js +31 -0
  24. package/template-mongo/api/models/User.js +28 -0
  25. package/template-mongo/api/routes.js +99 -0
  26. package/template-mongo/env-sample.txt +3 -0
  27. package/template-mongo/eslint.config.js +29 -0
  28. package/template-mongo/index.html +13 -0
  29. package/template-mongo/package-lock.json +5076 -0
  30. package/template-mongo/package.json +38 -0
  31. package/template-mongo/public/rex logo.png +0 -0
  32. package/template-mongo/public/rex.png +0 -0
  33. package/template-mongo/public/vite.svg +1 -0
  34. package/template-mongo/server.js +80 -0
  35. package/template-mongo/src/App.css +42 -0
  36. package/template-mongo/src/App.jsx +42 -0
  37. package/template-mongo/src/assets/react.svg +1 -0
  38. package/template-mongo/src/components/Layout.jsx +85 -0
  39. package/template-mongo/src/components/PublicOnly.jsx +17 -0
  40. package/template-mongo/src/components/RequireAuth.jsx +20 -0
  41. package/template-mongo/src/context/AuthContext.jsx +35 -0
  42. package/template-mongo/src/hooks/useAuthContext.js +11 -0
  43. package/template-mongo/src/hooks/useLogin.js +42 -0
  44. package/template-mongo/src/hooks/useSignup.js +23 -0
  45. package/template-mongo/src/hooks/useTest.js +18 -0
  46. package/template-mongo/src/index.css +1 -0
  47. package/template-mongo/src/main.jsx +18 -0
  48. package/template-mongo/src/pages/Docs.jsx +131 -0
  49. package/template-mongo/src/pages/Home.jsx +93 -0
  50. package/template-mongo/src/pages/Login.jsx +97 -0
  51. package/template-mongo/src/pages/Signup.jsx +112 -0
  52. package/template-mongo/vercel.json +13 -0
  53. package/template-mongo/vite.config.js +7 -0
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "the-react-experiment",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "nodemon --watch server.js --watch api server.js",
8
+ "build": "vite build",
9
+ "lint": "eslint .",
10
+ "preview": "vite preview"
11
+ },
12
+ "dependencies": {
13
+ "@tailwindcss/vite": "^4.2.0",
14
+ "@tanstack/react-query": "^5.90.21",
15
+ "axios": "^1.13.5",
16
+ "express": "^5.2.1",
17
+ "react": "^19.2.4",
18
+ "react-dom": "^19.2.4",
19
+ "tailwindcss": "^4.2.0"
20
+ },
21
+ "devDependencies": {
22
+ "@eslint/js": "^10.0.1",
23
+ "@types/react": "^19.2.14",
24
+ "@types/react-dom": "^19.2.3",
25
+ "@vitejs/plugin-react": "^5.1.4",
26
+ "eslint": "^10.0.1",
27
+ "eslint-plugin-react-hooks": "^7.0.1",
28
+ "eslint-plugin-react-refresh": "^0.5.0",
29
+ "globals": "^17.3.0",
30
+ "nodemon": "^3.1.13",
31
+ "vite": "^7.3.1"
32
+ }
33
+ }
Binary file
Binary file
@@ -0,0 +1,77 @@
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+ import { fileURLToPath } from 'url'
4
+ import express from 'express'
5
+ import apiRoutes from './api/routes.js'
6
+
7
+ // 1. Setup Constants
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
9
+ const app = express()
10
+ const isProduction = process.env.NODE_ENV === 'production'
11
+ const PORT = process.env.PORT || 5173
12
+
13
+
14
+ // 2. Global Middleware (JSON parser is usually good to have)
15
+ app.use(express.json())
16
+
17
+ // 3. API Routes (Always load these first!)
18
+ app.use('/api', apiRoutes)
19
+
20
+ async function createServer(app) {
21
+
22
+
23
+ // ---------------------------------------------------------
24
+ // 🚀 PRODUCTION MODE (Vercel / Render)
25
+ // ---------------------------------------------------------
26
+ if (isProduction) {
27
+ console.log('🚀 Running in PRODUCTION mode')
28
+
29
+ // Serve static files from the 'dist' folder
30
+ app.use(express.static(path.resolve(__dirname, 'dist')))
31
+
32
+ // SPA Fallback: The Regex Fix 💊
33
+ // Matches ANY route and sends index.html
34
+ app.get(/.*/, (req, res) => {
35
+ res.sendFile(path.resolve(__dirname, 'dist', 'index.html'))
36
+ })
37
+ }
38
+
39
+ // ---------------------------------------------------------
40
+ // 🛠️ DEVELOPMENT MODE (Localhost)
41
+ // ---------------------------------------------------------
42
+ else {
43
+ console.log('🛠️ Running in DEVELOPMENT mode')
44
+
45
+ // Dynamic import: Only load Vite in dev mode!
46
+ const { createServer: createViteServer } = await import('vite')
47
+
48
+ const vite = await createViteServer({
49
+ server: { middlewareMode: true },
50
+ appType: 'custom'
51
+ })
52
+
53
+ app.use(vite.middlewares)
54
+
55
+ app.use(async (req, res, next) => {
56
+ const url = req.originalUrl
57
+ try {
58
+ let template = fs.readFileSync(path.resolve(__dirname, 'index.html'), 'utf-8')
59
+ template = await vite.transformIndexHtml(url, template)
60
+ res.status(200).set({ 'Content-Type': 'text/html' }).end(template)
61
+ } catch (e) {
62
+ vite.ssrFixStacktrace(e)
63
+ next(e)
64
+ }
65
+ })
66
+ }
67
+
68
+ // 4. Start Server
69
+ app.listen(PORT, () => {
70
+ console.log(`Server running at http://localhost:${PORT}`)
71
+ })
72
+ }
73
+
74
+ createServer(app)
75
+
76
+
77
+ export default app
@@ -0,0 +1,42 @@
1
+ #root {
2
+ max-width: 1280px;
3
+ margin: 0 auto;
4
+ padding: 2rem;
5
+ text-align: center;
6
+ }
7
+
8
+ .logo {
9
+ height: 6em;
10
+ padding: 1.5em;
11
+ will-change: filter;
12
+ transition: filter 300ms;
13
+ }
14
+ .logo:hover {
15
+ filter: drop-shadow(0 0 2em #646cffaa);
16
+ }
17
+ .logo.react:hover {
18
+ filter: drop-shadow(0 0 2em #61dafbaa);
19
+ }
20
+
21
+ @keyframes logo-spin {
22
+ from {
23
+ transform: rotate(0deg);
24
+ }
25
+ to {
26
+ transform: rotate(360deg);
27
+ }
28
+ }
29
+
30
+ @media (prefers-reduced-motion: no-preference) {
31
+ a:nth-of-type(2) .logo {
32
+ animation: logo-spin infinite 20s linear;
33
+ }
34
+ }
35
+
36
+ .card {
37
+ padding: 2em;
38
+ }
39
+
40
+ .read-the-docs {
41
+ color: #888;
42
+ }
@@ -0,0 +1,326 @@
1
+ import { useTest } from './hooks/useTest'
2
+ import { useState, useEffect } from "react";
3
+
4
+ const features = [
5
+ {
6
+ icon: (
7
+ <svg viewBox="0 0 24 24" fill="none" className="w-6 h-6" stroke="currentColor" strokeWidth={1.5}>
8
+ <path strokeLinecap="round" strokeLinejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z" />
9
+ </svg>
10
+ ),
11
+ title: "Lightning Fast",
12
+ desc: "Vite-powered HMR with Express acting as the boss. Zero cold start, instant feedback loop.",
13
+ },
14
+ {
15
+ icon: (
16
+ <svg viewBox="0 0 24 24" fill="none" className="w-6 h-6" stroke="currentColor" strokeWidth={1.5}>
17
+ <path strokeLinecap="round" strokeLinejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
18
+ </svg>
19
+ ),
20
+ title: "One House",
21
+ desc: "Frontend & backend unified. No proxy configs, no CORS headaches. Just one coherent project.",
22
+ },
23
+ {
24
+ icon: (
25
+ <svg viewBox="0 0 24 24" fill="none" className="w-6 h-6" stroke="currentColor" strokeWidth={1.5}>
26
+ <path strokeLinecap="round" strokeLinejoin="round" d="M14.25 9.75L16.5 12l-2.25 2.25m-4.5 0L7.5 12l2.25-2.25M6 20.25h12A2.25 2.25 0 0020.25 18V6A2.25 2.25 0 0018 3.75H6A2.25 2.25 0 003.75 6v12A2.25 2.25 0 006 20.25z" />
27
+ </svg>
28
+ ),
29
+ title: "Express + React",
30
+ desc: "Full power of Express.js for your API routes, with React + Vite for the frontend. Best of both.",
31
+ },
32
+ {
33
+ icon: (
34
+ <svg viewBox="0 0 24 24" fill="none" className="w-6 h-6" stroke="currentColor" strokeWidth={1.5}>
35
+ <path strokeLinecap="round" strokeLinejoin="round" d="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z" />
36
+ </svg>
37
+ ),
38
+ title: "Production Ready",
39
+ desc: "Unified build system. Ship your entire app — API and UI — as one deployable unit.",
40
+ },
41
+ ];
42
+
43
+ const steps = [
44
+ { cmd: "npx create-rex-app my-app", label: "Scaffold your app" },
45
+ { cmd: "cd my-app && npm install", label: "Install dependencies" },
46
+ { cmd: "npm run dev", label: "Start developing" },
47
+ ];
48
+
49
+ function AnimatedCounter({ target, suffix = "" }) {
50
+ const [count, setCount] = useState(0);
51
+ useEffect(() => {
52
+ if (target === 0) return;
53
+ let start = 0;
54
+ const step = Math.ceil(target / 40);
55
+ const timer = setInterval(() => {
56
+ start += step;
57
+ if (start >= target) { setCount(target); clearInterval(timer); }
58
+ else setCount(start);
59
+ }, 30);
60
+ return () => clearInterval(timer);
61
+ }, [target]);
62
+ return <span>{count}{suffix}</span>;
63
+ }
64
+
65
+ // Live server status badge — powered by useTest hook → GET /api/test
66
+ function ServerStatusBadge() {
67
+ const { data, isLoading, isError, error } = useTest();
68
+
69
+ if (isLoading) {
70
+ return (
71
+ <div className="inline-flex items-center gap-2 bg-white/[0.04] border border-white/10 rounded-full px-4 py-1.5 text-xs text-white/50 mb-8">
72
+ <span className="w-1.5 h-1.5 rounded-full bg-yellow-400 animate-pulse" />
73
+ Connecting to server…
74
+ </div>
75
+ );
76
+ }
77
+
78
+ if (isError) {
79
+ return (
80
+ <div className="inline-flex items-center gap-2 bg-red-500/[0.08] border border-red-500/20 rounded-full px-4 py-1.5 text-xs text-red-400 mb-8">
81
+ <span className="w-1.5 h-1.5 rounded-full bg-red-500" />
82
+ Server offline — {error?.message ?? "Connection failed"}
83
+ </div>
84
+ );
85
+ }
86
+
87
+ // Handles both plain string responses and { message: "..." } shaped objects
88
+ const msg =
89
+ typeof data === "string"
90
+ ? data
91
+ : typeof data?.message === "string"
92
+ ? data.message
93
+ : "Server connected";
94
+
95
+ return (
96
+ <div className="inline-flex items-center gap-2 bg-emerald-500/[0.08] border border-emerald-500/20 rounded-full px-4 py-1.5 text-xs text-emerald-400 mb-8">
97
+ <span className="w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse" />
98
+ {msg}
99
+ </div>
100
+ );
101
+ }
102
+
103
+ export default function App() {
104
+ const [copied, setCopied] = useState(null);
105
+
106
+ const copy = (text, idx) => {
107
+ navigator.clipboard.writeText(text);
108
+ setCopied(idx);
109
+ setTimeout(() => setCopied(null), 2000);
110
+ };
111
+
112
+ return (
113
+ <div className="min-h-screen bg-[#0a0a0f] text-white font-[system-ui] overflow-x-hidden">
114
+ {/* Ambient background */}
115
+ <div className="fixed inset-0 pointer-events-none z-0">
116
+ <div className="absolute top-[-20%] left-[10%] w-[600px] h-[600px] rounded-full bg-[#ff4d00] opacity-[0.06] blur-[120px]" />
117
+ <div className="absolute bottom-[-10%] right-[5%] w-[500px] h-[500px] rounded-full bg-[#ff7a38] opacity-[0.05] blur-[100px]" />
118
+ <div className="absolute top-[40%] left-[40%] w-[300px] h-[300px] rounded-full bg-[#ff4d00] opacity-[0.04] blur-[80px]" />
119
+ </div>
120
+
121
+ {/* Grid overlay */}
122
+ <div
123
+ className="fixed inset-0 z-0 pointer-events-none opacity-[0.03]"
124
+ style={{
125
+ backgroundImage:
126
+ "linear-gradient(#ff4d00 1px, transparent 1px), linear-gradient(90deg, #ff4d00 1px, transparent 1px)",
127
+ backgroundSize: "60px 60px",
128
+ }}
129
+ />
130
+
131
+ {/* Nav */}
132
+ <nav className="relative z-10 flex items-center justify-between px-6 md:px-12 py-5 border-b border-white/[0.06]">
133
+ <div className="flex items-center gap-3">
134
+ <img src="/rex.png" alt="Rex" className="h-8 w-auto drop-shadow-[0_0_20px_rgba(255,77,0,0.4)]" />
135
+ <span className="text-lg font-bold tracking-tight text-white/90">rex</span>
136
+ <span className="ml-1 text-[10px] font-semibold uppercase tracking-widest bg-[#ff4d00]/20 text-[#ff6a2a] border border-[#ff4d00]/30 px-2 py-0.5 rounded-full">
137
+ v1.0
138
+ </span>
139
+ </div>
140
+ <div className="hidden md:flex items-center gap-8 text-sm text-white/50">
141
+ <a href="https://rex-docs.vercel.app/" className="hover:text-white transition-colors">Docs</a>
142
+ {/* <a href="#" className="hover:text-white transition-colors">Examples</a>
143
+ <a href="#" className="hover:text-white transition-colors">Changelog</a> */}
144
+ </div>
145
+ <a
146
+ href="https://github.com/saumyarawal-webdev"
147
+ className="flex items-center gap-2 text-sm bg-[#ff4d00] hover:bg-[#ff6a2a] text-white px-4 py-2 rounded-lg font-medium transition-all duration-200 hover:shadow-[0_0_20px_rgba(255,77,0,0.4)]"
148
+ >
149
+ <svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
150
+ <path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
151
+ </svg>
152
+ GitHub
153
+ </a>
154
+ </nav>
155
+
156
+ {/* Hero */}
157
+ <section className="relative z-10 flex flex-col items-center text-center px-6 pt-24 pb-20 md:pt-32 md:pb-28">
158
+
159
+ {/* Live server status — driven by useTest → /api/test */}
160
+ <ServerStatusBadge />
161
+
162
+ <div className="flex justify-center mb-8">
163
+ <img
164
+ src="/rex.png"
165
+ alt="Rex Logo"
166
+ className="h-24 md:h-32 w-auto drop-shadow-[0_0_40px_rgba(255,77,0,0.35)]"
167
+ />
168
+ </div>
169
+
170
+ <h1 className="text-5xl md:text-7xl lg:text-8xl font-black tracking-tight leading-none mb-6">
171
+ <span className="text-white">Build Faster.</span>
172
+ <br />
173
+ <span className="bg-gradient-to-r from-[#ff4d00] via-[#ff6a2a] to-[#ffaa44] bg-clip-text text-transparent">
174
+ Ship Together.
175
+ </span>
176
+ </h1>
177
+
178
+ <p className="text-lg md:text-xl text-white/50 max-w-2xl mb-10 leading-relaxed">
179
+ Rex unifies React + Vite and Express.js under one roof — 10× faster than alternatives.
180
+ No more context switching between frontend and backend.
181
+ </p>
182
+
183
+ <div className="flex flex-col sm:flex-row items-center gap-4">
184
+ <a
185
+ href="https://rex-docs.vercel.app/"
186
+ className="w-full sm:w-auto bg-[#ff4d00] hover:bg-[#ff6a2a] text-white font-semibold px-8 py-3.5 rounded-xl transition-all duration-200 hover:shadow-[0_0_30px_rgba(255,77,0,0.5)] text-sm"
187
+ >
188
+ Read the Docs →
189
+ </a>
190
+ {/* <a
191
+ href="#"
192
+ className="w-full sm:w-auto bg-white/[0.05] hover:bg-white/[0.08] border border-white/10 text-white/80 font-semibold px-8 py-3.5 rounded-xl transition-all duration-200 text-sm"
193
+ >
194
+ View Examples
195
+ </a> */}
196
+ </div>
197
+
198
+ {/* Stats */}
199
+ <div className="mt-20 grid grid-cols-3 gap-6 md:gap-16 w-full max-w-lg">
200
+ {[
201
+ { val: 10, suffix: "×", label: "Faster" },
202
+ { val: 1, suffix: "", label: "Unified Codebase" },
203
+ { val: 0, suffix: " ms", label: "Config to Start" },
204
+ ].map((s, i) => (
205
+ <div key={i} className="flex flex-col items-center">
206
+ <span className="text-3xl md:text-4xl font-black text-[#ff4d00]">
207
+ <AnimatedCounter target={s.val} suffix={s.suffix} />
208
+ </span>
209
+ <span className="text-xs text-white/40 mt-1">{s.label}</span>
210
+ </div>
211
+ ))}
212
+ </div>
213
+ </section>
214
+
215
+ {/* Architecture Banner */}
216
+ <section className="relative z-10 mx-4 md:mx-12 mb-20 rounded-2xl border border-white/[0.07] bg-white/[0.02] backdrop-blur-sm overflow-hidden">
217
+ <div className="absolute inset-0 bg-gradient-to-br from-[#ff4d00]/5 to-transparent" />
218
+ <div className="relative p-8 md:p-12">
219
+ <p className="text-xs uppercase tracking-[0.25em] text-[#ff6a2a] mb-4 font-semibold">Architecture</p>
220
+ <div className="flex flex-col md:flex-row items-center justify-center gap-4 md:gap-6 text-sm">
221
+ {["React + Vite (Frontend)", "→", "Vite Middleware", "→", "Express.js (Boss)", "→", "Your API"].map((item, i) => (
222
+ <div key={i}>
223
+ {item === "→" ? (
224
+ <span className="text-[#ff4d00]/60 text-lg font-light hidden md:block">→</span>
225
+ ) : (
226
+ <div className="bg-white/[0.05] border border-white/[0.08] rounded-lg px-4 py-2 text-white/70 font-mono text-xs md:text-sm text-center">
227
+ {item}
228
+ </div>
229
+ )}
230
+ </div>
231
+ ))}
232
+ </div>
233
+ <p className="text-center text-white/30 text-xs mt-6">One process. One port. Full control.</p>
234
+ </div>
235
+ </section>
236
+
237
+ {/* Features */}
238
+ <section className="relative z-10 px-6 md:px-12 mb-24">
239
+ <p className="text-xs uppercase tracking-[0.25em] text-[#ff6a2a] mb-3 font-semibold text-center">Why Rex</p>
240
+ <h2 className="text-3xl md:text-4xl font-bold text-center mb-12 text-white/90">
241
+ Everything you need. Nothing you don't.
242
+ </h2>
243
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 max-w-6xl mx-auto">
244
+ {features.map((f, i) => (
245
+ <div
246
+ key={i}
247
+ className="group relative bg-white/[0.03] hover:bg-white/[0.06] border border-white/[0.07] hover:border-[#ff4d00]/30 rounded-2xl p-6 transition-all duration-300"
248
+ >
249
+ <div className="text-[#ff4d00] mb-4 group-hover:scale-110 transition-transform duration-200">
250
+ {f.icon}
251
+ </div>
252
+ <h3 className="font-semibold text-white/90 mb-2">{f.title}</h3>
253
+ <p className="text-sm text-white/40 leading-relaxed">{f.desc}</p>
254
+ </div>
255
+ ))}
256
+ </div>
257
+ </section>
258
+
259
+ {/* Get Started */}
260
+ <section className="relative z-10 px-6 md:px-12 mb-24">
261
+ <p className="text-xs uppercase tracking-[0.25em] text-[#ff6a2a] mb-3 font-semibold text-center">Quick Start</p>
262
+ <h2 className="text-3xl md:text-4xl font-bold text-center mb-12 text-white/90">
263
+ Up and running in seconds.
264
+ </h2>
265
+ <div className="max-w-2xl mx-auto space-y-3">
266
+ {steps.map((step, i) => (
267
+ <div
268
+ key={i}
269
+ className="flex items-center gap-4 bg-white/[0.03] border border-white/[0.07] rounded-xl px-5 py-4 group hover:border-white/[0.15] transition-all"
270
+ >
271
+ <span className="w-7 h-7 rounded-full bg-[#ff4d00]/20 text-[#ff6a2a] text-xs font-bold flex items-center justify-center shrink-0 border border-[#ff4d00]/30">
272
+ {i + 1}
273
+ </span>
274
+ <code className="flex-1 font-mono text-sm text-white/70">{step.cmd}</code>
275
+ <button
276
+ onClick={() => copy(step.cmd, i)}
277
+ className="shrink-0 text-white/30 hover:text-white/70 transition-colors"
278
+ title="Copy"
279
+ >
280
+ {copied === i ? (
281
+ <svg className="w-4 h-4 text-[#ff6a2a]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
282
+ <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
283
+ </svg>
284
+ ) : (
285
+ <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
286
+ <path strokeLinecap="round" strokeLinejoin="round" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
287
+ </svg>
288
+ )}
289
+ </button>
290
+ </div>
291
+ ))}
292
+ </div>
293
+ </section>
294
+
295
+ {/* Edit hint */}
296
+ <section className="relative z-10 px-6 md:px-12 mb-24 flex justify-center">
297
+ <div className="bg-white/[0.02] border border-white/[0.07] rounded-2xl px-8 py-6 text-center max-w-md">
298
+ <p className="text-white/50 text-sm leading-relaxed">
299
+ Edit{" "}
300
+ <code className="bg-white/[0.08] text-[#ff6a2a] px-1.5 py-0.5 rounded text-xs font-mono">
301
+ src/App.jsx
302
+ </code>{" "}
303
+ to get started. Changes appear instantly thanks to Vite HMR.
304
+ </p>
305
+ </div>
306
+ </section>
307
+
308
+ {/* Footer */}
309
+ <footer className="relative z-10 border-t border-white/[0.06] px-6 md:px-12 py-8 flex flex-col md:flex-row items-center justify-between gap-4 text-sm text-white/30">
310
+ <div className="flex items-center gap-2">
311
+ <img src="/rex.png" alt="Rex" className="h-5 w-auto opacity-50" />
312
+ <span>Rex Framework</span>
313
+ </div>
314
+ <p>
315
+ Built with{" "}
316
+ <span className="text-[#ff4d00]">♥</span> using React, Vite & Express
317
+ </p>
318
+ <div className="flex gap-6">
319
+ <a href="https://rex-docs.vercel.app/" className="hover:text-white/70 transition-colors">Docs</a>
320
+ <a href="https://github.com/saumyarawal-webdev" className="hover:text-white/70 transition-colors">GitHub</a>
321
+ <a href="#" className="hover:text-white/70 transition-colors">License</a>
322
+ </div>
323
+ </footer>
324
+ </div>
325
+ );
326
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
@@ -0,0 +1,18 @@
1
+ import axios from 'axios'
2
+ import { useQuery } from '@tanstack/react-query'
3
+
4
+ // The Fetcher (Private to this file mostly, or exported if needed elsewhere)
5
+ const fetchServerMessage = async () => {
6
+ const response = await axios.get('/api/test')
7
+ return response.data
8
+ }
9
+
10
+ // The Custom Hook
11
+ export const useTest = () => {
12
+ return useQuery({
13
+ queryKey: ['serverMessage'],
14
+ queryFn: fetchServerMessage,
15
+ // You can add default options here if you want (e.g., staleTime)
16
+ staleTime: 1000 * 60 * 5, // Data is fresh for 5 minutes
17
+ })
18
+ }
@@ -0,0 +1 @@
1
+ @import "tailwindcss";
@@ -0,0 +1,14 @@
1
+ import { StrictMode } from 'react'
2
+ import { createRoot } from 'react-dom/client'
3
+ import './index.css'
4
+ import App from './App.jsx'
5
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
6
+
7
+ const queryClient = new QueryClient()
8
+ createRoot(document.getElementById('root')).render(
9
+ <StrictMode>
10
+ <QueryClientProvider client={queryClient}>
11
+ <App />
12
+ </QueryClientProvider>
13
+ </StrictMode>,
14
+ )
@@ -0,0 +1,8 @@
1
+ {
2
+ "rewrites": [
3
+ {
4
+ "source": "/api/(.*)",
5
+ "destination": "/server.js"
6
+ }
7
+ ]
8
+ }
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import tailwindcss from '@tailwindcss/vite'
4
+ // https://vite.dev/config/
5
+ export default defineConfig({
6
+ plugins: [react(),tailwindcss()],
7
+ })
@@ -0,0 +1,96 @@
1
+ # 🚂 React Express Boilerplate + MongoDB (Auth Ready)
2
+
3
+ A robust, "Monolith-style" fullstack boilerplate that unifies **Vite (React)** and **Express** into a single development experience.
4
+
5
+ Pre-configured with **MongoDB** connection, **JWT Authentication**, and **Protected Routes**.
6
+
7
+ > **One Terminal. One Port. Fullstack Power.**
8
+
9
+ ## 🚀 Features
10
+
11
+ * **Unified Server:** `server.js` acts as the single entry point. It serves the API *and* the Frontend (via Vite middleware).
12
+ * **🔐 Secure Authentication:** Complete Login/Signup flow using **JWT (JSON Web Tokens)** and `bcryptjs` for password hashing.
13
+ * **🛡️ Protected Routes:** Specialized `RequireAuth` wrapper component to guard pages from unauthenticated users.
14
+ * **🗄️ MongoDB Ready:** Mongoose setup with a dedicated `User` model and schema.
15
+ * **Global State:** Lightweight **Context API** for managing User/Auth state.
16
+ * **Data Fetching:** Pre-configured with **Axios** and **TanStack Query (React Query)** for professional-grade state management.
17
+ * **Modern UI:** Styled with **Tailwind CSS**.
18
+
19
+ ## 🛠️ Tech Stack
20
+
21
+ * **Frontend:** React, Vite, Tailwind CSS
22
+ * **Backend:** Express.js, Node.js
23
+ * **Database:** MongoDB (Mongoose ODM)
24
+ * **Auth:** JWT, bcryptjs
25
+ * **State:** Context API (Auth), TanStack Query (Server State)
26
+
27
+ ## 📂 Project Structure
28
+
29
+ ```text
30
+ root/
31
+ ├── .env # Secrets (DB URI, JWT Secret) - CREATE THIS!
32
+ ├── server.js # The Engine (Express + Vite Middleware)
33
+ ├── api/ # Backend Logic
34
+ │ ├── db.js # Database Connection
35
+ │ ├── routes.js # API Endpoints (Auth + User)
36
+ │ ├── models/ # Mongoose Schemas (User.js)
37
+ │ └── middleware/ # Auth Guard (authMiddleware.js)
38
+
39
+ └── src/ # React Frontend
40
+ ├── main.jsx # Entry point (Providers wrap App here)
41
+ ├── App.jsx # Router & Route Definitions
42
+ ├── context/ # Global State (AuthContext.jsx)
43
+ ├── components/
44
+ │ ├── Layout.jsx # Navbar & Footer
45
+ │ └── RequireAuth.jsx # 🔒 The Protected Route Guard
46
+ ├── hooks/ # Custom Hooks (useLogin, useSignup)
47
+ └── pages/ # Views (Home, Login, Docs, etc.)
48
+ ```
49
+
50
+
51
+ ## 🏁 Getting Started
52
+
53
+ ### 1. Clone & Install
54
+ ```bash
55
+ git clone [https://github.com/saumyarawal-webdev/react-express-nosql.git](https://github.com/saumyarawal-webdev/react-express-nosql.git)
56
+ cd react-express-boilerplate
57
+ npm install
58
+ ```
59
+ ### 2. Start the Engine 🚂
60
+ Run the development environment. This starts the Express server and the Vite middleware together.
61
+
62
+ ```bash
63
+ npm run dev
64
+ ```
65
+ * **Frontend:** `http://localhost:5173`
66
+ * **API Endpoint:** `http://localhost:5173/api/test`
67
+ * **Refer env-sample** for configuring database.
68
+ ### 3. Build for Production
69
+ When ready to deploy, build the React app. The server is configured to serve the static files in production (requires minor `dist` folder config in server.js for final deploy).
70
+
71
+ ```bash
72
+ npm run build
73
+ ```
74
+
75
+ ## 🔒 Authentication Architecture
76
+ * **Signup**: Hashes password via `bcrypt` and saves user to `MongoDB`.
77
+
78
+ * **Login**: Validates credentials and returns a `JWT Token`.
79
+
80
+ * **Client**: Stores `Token` in localStorage and updates AuthContext.
81
+
82
+ * **Requests**: `useLogin` hook automatically fetches User Profile after login to populate UI.
83
+
84
+ * **Protection**: Routes wrapped in `<RequireAuth />` check for the user in Context. If missing, they redirect to Login.
85
+
86
+ ## 🧩 How to Add a New API
87
+
88
+ 1. **Backend:** Add a new route in `api/routes.js`:
89
+ ```javascript
90
+ router.get('/users', (req, res) => { ... })
91
+ ```
92
+ 2. **Frontend:** Create a hook in `src/hooks/useUsers.js` using Axios + useQuery.
93
+ 3. **UI:** Import the hook in your component and display the data.
94
+
95
+ ---
96
+ *Made with 💙 by Saumya*
@@ -0,0 +1,17 @@
1
+ import mongoose from 'mongoose';
2
+
3
+ const connectDB = async () => {
4
+ try {
5
+ // Attempt to connect to the database
6
+ const conn = await mongoose.connect(process.env.MONGO_URI);
7
+
8
+ // If successful, bark!
9
+ console.log(`🍃 MongoDB Connected`);
10
+ } catch (error) {
11
+ // If failed, whimper and exit
12
+ console.error(`❌ Error: ${error.message}`);
13
+ process.exit(1);
14
+ }
15
+ };
16
+
17
+ export default connectDB;