@mr.dj2u/knowledge 0.1.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.
- package/dist/content/checklists/ship-test-loop.md +16 -0
- package/dist/content/checklists/unified-agent-bundle-validation.md +8 -0
- package/dist/content/examples/ship-test-loop.md +13 -0
- package/dist/content/examples/unified-agent-bundle-bootstrap.md +8 -0
- package/dist/content/guides/animation-performance.md +30 -0
- package/dist/content/guides/post-create-onboarding.md +113 -0
- package/dist/content/patterns/api/api-routes.md +314 -0
- package/dist/content/patterns/api/error-handling.md +311 -0
- package/dist/content/patterns/database/drizzle-schema.md +280 -0
- package/dist/content/patterns/database/migrations.md +365 -0
- package/dist/content/patterns/database/query-organization.md +537 -0
- package/dist/content/patterns/database/relations.md +450 -0
- package/dist/content/patterns/deployment/build-configuration.md +452 -0
- package/dist/content/patterns/deployment/ci-cd-patterns.md +448 -0
- package/dist/content/patterns/deployment/environment-config.md +380 -0
- package/dist/content/patterns/deployment/hosting-setup.md +425 -0
- package/dist/content/patterns/project/configuration-patterns.md +459 -0
- package/dist/content/patterns/project/documentation-org.md +506 -0
- package/dist/content/patterns/project/folder-structure.md +398 -0
- package/dist/content/patterns/project/library-exports.md +465 -0
- package/dist/content/patterns/project/monorepo-structure.md +500 -0
- package/dist/content/patterns/routing/dynamic-routes.md +221 -0
- package/dist/content/patterns/routing/file-based-routing.md +186 -0
- package/dist/content/patterns/routing/route-groups.md +429 -0
- package/dist/content/patterns/state/persistence-middleware.md +521 -0
- package/dist/content/patterns/state/selector-hooks.md +538 -0
- package/dist/content/patterns/state/store-organization.md +539 -0
- package/dist/content/patterns/state/zustand-patterns.md +348 -0
- package/dist/content/patterns/styling/component-styling.md +468 -0
- package/dist/content/patterns/styling/responsive-patterns.md +398 -0
- package/dist/content/patterns/styling/theme-configuration.md +426 -0
- package/dist/content/patterns/styling/uniwind-setup.md +412 -0
- package/dist/content/prompts/continue-development.md +27 -0
- package/dist/content/prompts/create-expo-super-stack.md +29 -0
- package/dist/content/prompts/fix-seo.md +29 -0
- package/dist/content/prompts/onboard-new-expo-app.md +11 -0
- package/dist/content/prompts/prepare-deploy.md +29 -0
- package/dist/content/prompts/project-research-plan.md +29 -0
- package/dist/content/prompts/review-expo-project.md +29 -0
- package/dist/content/prompts/run-doctor.md +30 -0
- package/dist/content/prompts/ship-test-loop.md +24 -0
- package/dist/content/reference/create-expo-stack-uniwind.md +29 -0
- package/dist/content/reference/doctor-dogfood.md +42 -0
- package/dist/content/reference/mcp-sdk-transport.md +30 -0
- package/dist/content/reference/package-ci-patterns.md +24 -0
- package/dist/content/reference/reference-repo-evacuation.md +31 -0
- package/dist/content/resource-index.json +67 -0
- package/dist/content/rules/app-folder-architecture.md +13 -0
- package/dist/content/rules/env-hygiene.md +9 -0
- package/dist/content/rules/seo-metadata.md +7 -0
- package/dist/content/rules/ssr-safety.md +9 -0
- package/dist/content/skills/api-routes.md +33 -0
- package/dist/content/skills/continue-development.md +31 -0
- package/dist/content/skills/debugging.md +31 -0
- package/dist/content/skills/deployment.md +32 -0
- package/dist/content/skills/dev-server-management.md +31 -0
- package/dist/content/skills/env-vars.md +32 -0
- package/dist/content/skills/expo-router-architecture.md +33 -0
- package/dist/content/skills/expo-ssr-safety.md +32 -0
- package/dist/content/skills/plugin-creation.md +41 -0
- package/dist/content/skills/production-server-patterns.md +31 -0
- package/dist/content/skills/project-onboarding.md +31 -0
- package/dist/content/skills/research-plan-intake.md +31 -0
- package/dist/content/skills/seo-metadata.md +31 -0
- package/dist/content/skills/super-stack-startup.md +31 -0
- package/dist/content/skills/uniwind-theming.md +32 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +369 -0
- package/dist/index.js.map +1 -0
- package/dist/patterns/index.d.ts +78 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/patterns/index.js +264 -0
- package/dist/patterns/index.js.map +1 -0
- package/dist/prompts/index.d.ts +35 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +270 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/skills/index.d.ts +3 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +2 -0
- package/dist/skills/index.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
# Configuration Patterns
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Configuration patterns establish consistent strategies for managing app configuration files across development, staging, and production environments. These patterns standardize `app.json`, `package.json`, build configs, and environment setup for reproducible, maintainable builds.
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
**Apply configuration patterns when:**
|
|
10
|
+
- ✅ Setting up a new Expo project
|
|
11
|
+
- ✅ Configuring iOS/Android builds
|
|
12
|
+
- ✅ Managing environment variables (dev/staging/prod)
|
|
13
|
+
- ✅ Optimizing build performance
|
|
14
|
+
- ✅ Enabling code transformations (Babel, Metro)
|
|
15
|
+
- ✅ Scaling to multiple platforms/app variants
|
|
16
|
+
|
|
17
|
+
## Core Concepts
|
|
18
|
+
|
|
19
|
+
**Configuration Hierarchy:**
|
|
20
|
+
```
|
|
21
|
+
1. app.json / app.config.js
|
|
22
|
+
└── Expo configuration (SDK, plugins, native config)
|
|
23
|
+
|
|
24
|
+
2. package.json
|
|
25
|
+
└── Dependencies, scripts, metadata
|
|
26
|
+
|
|
27
|
+
3. tsconfig.json
|
|
28
|
+
└── TypeScript compilation settings
|
|
29
|
+
|
|
30
|
+
4. metro.config.js
|
|
31
|
+
└── Metro bundler (module resolution, assets)
|
|
32
|
+
|
|
33
|
+
5. babel.config.js
|
|
34
|
+
└── Code transformation (async/await, decorators)
|
|
35
|
+
|
|
36
|
+
6. .env files
|
|
37
|
+
└── Runtime environment variables
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Code Examples
|
|
41
|
+
|
|
42
|
+
### app.json - Static Expo Configuration
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"expo": {
|
|
47
|
+
"name": "DJsPortfolio",
|
|
48
|
+
"slug": "djsportfolio",
|
|
49
|
+
"version": "1.0.0",
|
|
50
|
+
"scheme": "djsportfolio",
|
|
51
|
+
|
|
52
|
+
"platforms": ["ios", "android", "web"],
|
|
53
|
+
|
|
54
|
+
"sdkVersion": "55.0.0",
|
|
55
|
+
"runtimeVersion": "55.0.0",
|
|
56
|
+
|
|
57
|
+
"web": {
|
|
58
|
+
"output": "static",
|
|
59
|
+
"favicon": "./public/favicon.ico",
|
|
60
|
+
"bundler": "metro"
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
"ios": {
|
|
64
|
+
"bundleIdentifier": "com.mds2u.djsportfolio",
|
|
65
|
+
"buildNumber": "1",
|
|
66
|
+
"supportsTabletMode": true
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
"android": {
|
|
70
|
+
"package": "com.mds2u.djsportfolio",
|
|
71
|
+
"versionCode": 1,
|
|
72
|
+
"adaptiveIcon": {
|
|
73
|
+
"foregroundImage": "./public/adaptive-icon.png",
|
|
74
|
+
"backgroundImage": "./public/adaptive-icon-background.png"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
"plugins": [
|
|
79
|
+
[
|
|
80
|
+
"expo-camera",
|
|
81
|
+
{
|
|
82
|
+
"cameraPermission": "Allow DJsPortfolio to access camera"
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
[
|
|
86
|
+
"expo-splash-screen",
|
|
87
|
+
{
|
|
88
|
+
"image": "./public/splash.png",
|
|
89
|
+
"resizeMode": "contain"
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
],
|
|
93
|
+
|
|
94
|
+
"experiments": {
|
|
95
|
+
"typedRoutes": true,
|
|
96
|
+
"reactCompiler": true
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### app.config.js - Dynamic Expo Configuration
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
// app.config.js - Allows environment-based config
|
|
106
|
+
export default ({ config }) => {
|
|
107
|
+
const isDev = process.env.APP_ENV === 'development';
|
|
108
|
+
const isStaging = process.env.APP_ENV === 'staging';
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
...config,
|
|
112
|
+
|
|
113
|
+
name: isStaging ? 'DJsPortfolio (Staging)' : 'DJsPortfolio',
|
|
114
|
+
|
|
115
|
+
extra: {
|
|
116
|
+
apiUrl: isDev
|
|
117
|
+
? 'http://localhost:3000'
|
|
118
|
+
: isStaging
|
|
119
|
+
? 'https://staging-api.davidjgrimsley.com'
|
|
120
|
+
: 'https://api.davidjgrimsley.com',
|
|
121
|
+
|
|
122
|
+
environment: process.env.APP_ENV || 'development',
|
|
123
|
+
logLevel: isDev ? 'debug' : 'warn',
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
ios: {
|
|
127
|
+
bundleIdentifier: isDev
|
|
128
|
+
? 'com.mds2u.djsportfolio.dev'
|
|
129
|
+
: isStaging
|
|
130
|
+
? 'com.mds2u.djsportfolio.staging'
|
|
131
|
+
: 'com.mds2u.djsportfolio',
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
android: {
|
|
135
|
+
package: isDev
|
|
136
|
+
? 'com.mds2u.djsportfolio.dev'
|
|
137
|
+
: isStaging
|
|
138
|
+
? 'com.mds2u.djsportfolio.staging'
|
|
139
|
+
: 'com.mds2u.djsportfolio',
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### package.json - Dependencies & Scripts
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"name": "djsportfolio",
|
|
150
|
+
"version": "1.0.0",
|
|
151
|
+
"description": "Personal portfolio with React Native & Expo",
|
|
152
|
+
|
|
153
|
+
"type": "module",
|
|
154
|
+
"main": "index.js",
|
|
155
|
+
"private": true,
|
|
156
|
+
|
|
157
|
+
"scripts": {
|
|
158
|
+
"dev": "expo start",
|
|
159
|
+
"dev:ios": "expo start --ios",
|
|
160
|
+
"dev:android": "expo start --android",
|
|
161
|
+
"dev:web": "expo start --web",
|
|
162
|
+
|
|
163
|
+
"build": "expo build",
|
|
164
|
+
"build:web": "expo export -p web",
|
|
165
|
+
|
|
166
|
+
"lint": "eslint .",
|
|
167
|
+
"lint:fix": "eslint . --fix",
|
|
168
|
+
"type-check": "tsc --noEmit",
|
|
169
|
+
|
|
170
|
+
"test": "jest",
|
|
171
|
+
"test:watch": "jest --watch",
|
|
172
|
+
|
|
173
|
+
"eas:build:ios": "eas build --platform ios",
|
|
174
|
+
"eas:build:android": "eas build --platform android",
|
|
175
|
+
"eas:submit": "eas submit"
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
"dependencies": {
|
|
179
|
+
"react": "^19.0.0",
|
|
180
|
+
"react-native": "^0.83.0",
|
|
181
|
+
"expo": "^55.0.0",
|
|
182
|
+
"expo-router": "^3.0.0",
|
|
183
|
+
"zustand": "^4.4.0",
|
|
184
|
+
"drizzle-orm": "^0.30.0",
|
|
185
|
+
"@react-native-async-storage/async-storage": "^1.21.0"
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
"devDependencies": {
|
|
189
|
+
"@types/react": "^19.0.0",
|
|
190
|
+
"@types/react-native": "^0.83.0",
|
|
191
|
+
"typescript": "^5.9.0",
|
|
192
|
+
"eslint": "^9.0.0",
|
|
193
|
+
"@babel/core": "^7.23.0"
|
|
194
|
+
},
|
|
195
|
+
|
|
196
|
+
"engines": {
|
|
197
|
+
"node": ">=18.0.0",
|
|
198
|
+
"npm": ">=9.0.0"
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### tsconfig.json - TypeScript Configuration
|
|
204
|
+
|
|
205
|
+
```json
|
|
206
|
+
{
|
|
207
|
+
"extends": "@react-native/tsconfig",
|
|
208
|
+
|
|
209
|
+
"compilerOptions": {
|
|
210
|
+
"target": "ES2020",
|
|
211
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
|
212
|
+
"jsx": "react-jsx",
|
|
213
|
+
|
|
214
|
+
"strict": true,
|
|
215
|
+
"strictNullChecks": true,
|
|
216
|
+
"strictFunctionTypes": true,
|
|
217
|
+
"strictBindCallApply": true,
|
|
218
|
+
"strictPropertyInitialization": true,
|
|
219
|
+
"noImplicitThis": true,
|
|
220
|
+
"alwaysStrict": true,
|
|
221
|
+
"noUnusedLocals": true,
|
|
222
|
+
"noUnusedParameters": true,
|
|
223
|
+
"noImplicitReturns": true,
|
|
224
|
+
"noFallthroughCasesInSwitch": true,
|
|
225
|
+
|
|
226
|
+
"moduleResolution": "node",
|
|
227
|
+
"module": "ESNext",
|
|
228
|
+
"resolveJsonModule": true,
|
|
229
|
+
"allowSyntheticDefaultImports": true,
|
|
230
|
+
|
|
231
|
+
"baseUrl": ".",
|
|
232
|
+
"paths": {
|
|
233
|
+
"@/*": ["src/*"],
|
|
234
|
+
"@/assets/*": ["assets/*"]
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
"types": ["expo", "@react-native-types", "node"],
|
|
238
|
+
"skipLibCheck": true,
|
|
239
|
+
"forceConsistentCasingInFileNames": true
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
"include": ["src", "index.js"],
|
|
243
|
+
"exclude": ["node_modules", "dist", "build"]
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### metro.config.js - Module Resolution
|
|
248
|
+
|
|
249
|
+
```javascript
|
|
250
|
+
const { getDefaultConfig } = require('@react-native/metro-config');
|
|
251
|
+
const { withUniwindConfig } = require('uniwind/metro');
|
|
252
|
+
|
|
253
|
+
const config = getDefaultConfig(__dirname);
|
|
254
|
+
|
|
255
|
+
const customConfig = {
|
|
256
|
+
projectRoot: __dirname,
|
|
257
|
+
|
|
258
|
+
resolver: {
|
|
259
|
+
// Module resolution
|
|
260
|
+
assetExts: ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'wav', 'mp3'],
|
|
261
|
+
sourceExts: ['ts', 'tsx', 'js', 'jsx', 'json', 'mjs'],
|
|
262
|
+
|
|
263
|
+
// Platform-specific modules
|
|
264
|
+
platform: 'web',
|
|
265
|
+
|
|
266
|
+
// Alias resolution (alternative to tsconfig paths)
|
|
267
|
+
extraNodeModules: {
|
|
268
|
+
'@': `${__dirname}/src`,
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
transformer: {
|
|
273
|
+
// Enable CSS support via Uniwind
|
|
274
|
+
unstable_allowRequireContext: true,
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// Apply Uniwind configuration
|
|
279
|
+
module.exports = withUniwindConfig(customConfig, {
|
|
280
|
+
cssEntryFile: './src/global.css',
|
|
281
|
+
});
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### babel.config.js - Code Transformation
|
|
285
|
+
|
|
286
|
+
```javascript
|
|
287
|
+
module.exports = function (api) {
|
|
288
|
+
api.cache(true);
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
presets: [
|
|
292
|
+
'babel-preset-expo',
|
|
293
|
+
['@babel/preset-typescript', { allowDeclareFields: true }],
|
|
294
|
+
],
|
|
295
|
+
|
|
296
|
+
plugins: [
|
|
297
|
+
// React optimization (enabled by default in Expo SDK 54+)
|
|
298
|
+
['babel-plugin-react-compiler', { target: '19' }],
|
|
299
|
+
|
|
300
|
+
// Async/await support
|
|
301
|
+
'@babel/plugin-proposal-async-generator-functions',
|
|
302
|
+
|
|
303
|
+
// Decorators (if using)
|
|
304
|
+
['@babel/plugin-proposal-decorators', { loose: true }],
|
|
305
|
+
|
|
306
|
+
// Class properties
|
|
307
|
+
['@babel/plugin-proposal-class-properties', { loose: true }],
|
|
308
|
+
|
|
309
|
+
// Module resolution
|
|
310
|
+
['module-resolver', {
|
|
311
|
+
extensions: ['.ts', '.tsx', '.js', '.jsx'],
|
|
312
|
+
alias: {
|
|
313
|
+
'@': './src',
|
|
314
|
+
},
|
|
315
|
+
}],
|
|
316
|
+
],
|
|
317
|
+
};
|
|
318
|
+
};
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### .env Files - Environment Variables
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
# .env.development (local development)
|
|
325
|
+
EXPO_PUBLIC_API_URL=http://localhost:3000
|
|
326
|
+
EXPO_PUBLIC_LOG_LEVEL=debug
|
|
327
|
+
EXPO_PUBLIC_ENABLE_DEVTOOLS=true
|
|
328
|
+
|
|
329
|
+
DATABASE_URL=postgresql://dev:password@localhost:5432/djsportfolio_dev
|
|
330
|
+
JWT_SECRET=dev-secret-key-not-for-production
|
|
331
|
+
|
|
332
|
+
# .env.staging (staging environment)
|
|
333
|
+
EXPO_PUBLIC_API_URL=https://staging-api.davidjgrimsley.com
|
|
334
|
+
EXPO_PUBLIC_LOG_LEVEL=info
|
|
335
|
+
EXPO_PUBLIC_ENABLE_DEVTOOLS=false
|
|
336
|
+
|
|
337
|
+
DATABASE_URL=postgresql://user:password@staging-db:5432/djsportfolio_staging
|
|
338
|
+
JWT_SECRET=<from-secure-vault>
|
|
339
|
+
|
|
340
|
+
# .env.production (production)
|
|
341
|
+
EXPO_PUBLIC_API_URL=https://api.davidjgrimsley.com
|
|
342
|
+
EXPO_PUBLIC_LOG_LEVEL=warn
|
|
343
|
+
EXPO_PUBLIC_ENABLE_DEVTOOLS=false
|
|
344
|
+
|
|
345
|
+
DATABASE_URL=postgresql://user:password@prod-db:5432/djsportfolio
|
|
346
|
+
JWT_SECRET=<from-secure-vault>
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### .env.example - Template
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
# Public variables (exposed to client)
|
|
353
|
+
EXPO_PUBLIC_API_URL=https://api.example.com
|
|
354
|
+
EXPO_PUBLIC_LOG_LEVEL=warn
|
|
355
|
+
EXPO_PUBLIC_ENABLE_DEVTOOLS=false
|
|
356
|
+
|
|
357
|
+
# Private variables (server-only)
|
|
358
|
+
DATABASE_URL=postgresql://user:password@db:5432/app
|
|
359
|
+
JWT_SECRET=your-secret-key-here
|
|
360
|
+
STRIPE_SECRET_KEY=sk_test_...
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Configuration Loading (src/constants/config.ts)
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
// src/constants/config.ts - Centralized config access
|
|
367
|
+
import * as SecureStore from 'expo-secure-store';
|
|
368
|
+
|
|
369
|
+
export const PUBLIC_CONFIG = {
|
|
370
|
+
apiUrl: process.env.EXPO_PUBLIC_API_URL || 'http://localhost:3000',
|
|
371
|
+
logLevel: (process.env.EXPO_PUBLIC_LOG_LEVEL || 'warn') as LogLevel,
|
|
372
|
+
enableDevTools: process.env.EXPO_PUBLIC_ENABLE_DEVTOOLS === 'true',
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// Server-side only
|
|
376
|
+
export const PRIVATE_CONFIG = {
|
|
377
|
+
databaseUrl: process.env.DATABASE_URL,
|
|
378
|
+
jwtSecret: process.env.JWT_SECRET,
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
// Runtime validation
|
|
382
|
+
if (!PUBLIC_CONFIG.apiUrl) {
|
|
383
|
+
throw new Error('EXPO_PUBLIC_API_URL is required');
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Usage in components
|
|
387
|
+
export function useConfig() {
|
|
388
|
+
return PUBLIC_CONFIG;
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## Configuration Best Practices
|
|
393
|
+
|
|
394
|
+
### ✅ DO
|
|
395
|
+
|
|
396
|
+
1. **Use EXPO_PUBLIC_ prefix for client variables**
|
|
397
|
+
```bash
|
|
398
|
+
EXPO_PUBLIC_API_URL=... # ✅ Available in app
|
|
399
|
+
DATABASE_URL=... # ✅ Private, not exposed
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
2. **Separate environment configs**
|
|
403
|
+
```bash
|
|
404
|
+
.env.development
|
|
405
|
+
.env.staging
|
|
406
|
+
.env.production
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
3. **Validate config on startup**
|
|
410
|
+
```typescript
|
|
411
|
+
if (!process.env.EXPO_PUBLIC_API_URL) {
|
|
412
|
+
throw new Error('EXPO_PUBLIC_API_URL is required');
|
|
413
|
+
}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
4. **Use app.config.js for dynamic config**
|
|
417
|
+
```javascript
|
|
418
|
+
// Allows environment-based changes
|
|
419
|
+
const apiUrl = isDev ? 'http://localhost' : 'https://prod-api.com';
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### ❌ DON'T
|
|
423
|
+
|
|
424
|
+
1. **Don't expose secrets in client**
|
|
425
|
+
```bash
|
|
426
|
+
❌ EXPO_PUBLIC_API_KEY=sk_secret...
|
|
427
|
+
✅ API_KEY=sk_secret... (private)
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
2. **Don't commit .env files**
|
|
431
|
+
```bash
|
|
432
|
+
# .gitignore
|
|
433
|
+
.env
|
|
434
|
+
.env.*.local
|
|
435
|
+
.env.production.local
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
3. **Don't mix config sources**
|
|
439
|
+
```typescript
|
|
440
|
+
❌ process.env.API_URL sometimes, config.apiUrl other times
|
|
441
|
+
✅ Always use centralized config object
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
4. **Don't hardcode values in code**
|
|
445
|
+
```typescript
|
|
446
|
+
❌ const API_URL = 'https://api.example.com';
|
|
447
|
+
✅ const API_URL = process.env.EXPO_PUBLIC_API_URL;
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
## Related Patterns
|
|
451
|
+
|
|
452
|
+
- [Build Configuration](./build-configuration.md) — Metro, Babel, tsconfig
|
|
453
|
+
- [Environment Configuration](../deployment/environment-config.md) — Env setup
|
|
454
|
+
- [Folder Structure](./folder-structure.md) — File organization
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
*Pattern extracted from production repositories: DJsPortfolio, time2pay, not-hot-dog*
|
|
459
|
+
*Examples: app.json, app.config.js, metro.config.js, babel.config.js, tsconfig.json*
|