```js
const {Storage} = require('@google-cloud/storage');
const storage = new Storage({projectId: '[PROJECT-ID]'});
await storage.bucket('[BUCKET]').upload('/local/file', {
destination: 'path/to/object',
});
```
ADC: the Node client uses Application Default Credentials with no extra config. Locally that means `gcloud auth application-default login`; on Google Cloud it means the attached service account. If you pass `keyFilename`, that file wins over everything, which is usually not what you want in production code.
Upload notes:
- Uploads are resumable by default. For big files this is what you want; do not disable it to "simplify".
- Set `metadata.contentType` explicitly when serving files to browsers; GCS guesses from the extension but guesses wrong for less common types.
- Precondition equivalent: `ifGenerationMatch: 0` in the upload options prevents overwriting an existing object.
The signed URL trap (agents hit this weekly):
```js
const [url] = await storage.bucket('[BUCKET]').file('obj').getSignedUrl({expires: Date.now() + 3600e3});
```
This needs a private key to sign with. With a key file it works. With ADC from the metadata server (Cloud Run, GCE) it fails unless the service account has iam.serviceAccounts.signBlob, because signing becomes an IAM API call. The error looks like a permissions problem but it is a signing-identity problem. Fix: grant roles/iam.serviceAccountTokenCreator on the SA, or generate the URL from a workload that has a key.
Verify uploads with `gcloud storage ls` and test signed URLs with curl before handing them to users.