Dependencies, Secrets, and Error Handling
Modern JavaScript apps depend on many packages and services.
Security is not only about your own functions.
It also includes:
- package choices
- dependency updates
- secret handling
- error messages
- logging
- deployment settings
Choose Dependencies Carefully
Packages can save time.
They can also add risk.
Before adding a package, ask:
- Do we really need this dependency?
- Is it actively maintained?
- Does it have many unnecessary permissions or features?
- Is there a smaller built-in API that solves the problem?
- Does the team already use a similar package?
For example, use URLSearchParams instead of adding a package just to build query strings.
const params = new URLSearchParams({
q: "javascript security",
page: "1",
});Keep Dependencies Updated
Security fixes often arrive as package updates.
Use the tooling your project already has.
Common examples:
npm audit
npm outdatedDo not update packages blindly in a production app.
Read release notes, run tests, and pay attention to major version changes.
Lockfiles Matter
Files like package-lock.json, yarn.lock, and pnpm-lock.yaml record exact dependency versions.
They help teams install the same dependency tree.
Commit lockfiles for apps.
Review unexpected lockfile changes carefully.
Secrets Do Not Belong in Frontend Code
Frontend code is visible to users.
These should not be placed in browser JavaScript:
- private API keys
- database URLs
- signing secrets
- service account credentials
- production tokens
Environment variables in frontend build tools may still be bundled into public JavaScript if they are used by client code.
Only expose values that are meant to be public.
Public Config vs Secret Config
Some values are safe to expose.
Examples:
- public analytics IDs
- public API base URLs
- feature flags that do not reveal sensitive logic
Secrets must stay on the server.
If the frontend needs an action that requires a secret, call your backend.
await fetch("/api/send-invite", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ email }),
});The backend can validate the request, check permissions, and use the secret safely.
Safe Error Handling
Error messages should help users without revealing too much.
try {
await savePaymentMethod(formData);
showMessage("Payment method saved.");
} catch (error) {
console.error(error);
showError("We could not save your payment method. Please try again.");
}The user sees a helpful general message.
The developer logs the detailed error.
Do Not Leak Sensitive Details
Avoid showing raw internal errors to users.
Risky details include:
- stack traces
- SQL queries
- file paths
- access tokens
- private request headers
- internal service names
During development, detailed errors are useful.
In production, show safer messages and send details to secure logs.
Be Careful with Logs
Logs are useful for debugging.
They can also leak private data.
Avoid logging:
- passwords
- full tokens
- session cookies
- credit card data
- sensitive personal information
If you need to identify a token or ID in logs, use a short prefix or a separate safe identifier.
Common Mistakes
Do not add a package when a platform API already solves the problem.
Do not ignore dependency warnings forever.
Do not commit real secrets.
Do not assume frontend environment variables are private.
Do not show raw server errors to users in production.
Do not log sensitive values.
Summary
Dependencies, secrets, and errors are part of application security.
Use fewer dependencies when reasonable, keep packages updated, keep secrets on the server, and show users safe error messages.