DEV Community

Jennifer Davis for Google Cloud

Posted on

Why your `curl` logic just bit you 🐾

It’s a common strategy to test a new API directly with curl. It feels intuitive, fast, and removes the overhead of a language runtime. For example, if you are testing out the Google Cloud Logging API, you might start with a simple request to list logs from a Kubernetes container.:

curl -X POST \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  https://logging.googleapis.com/v2/entries:list \
  -d '{
    "resourceNames": ["projects/your-project-id"],
    "filter": "resource.type=\"k8s_container\""
  }'
Enter fullscreen mode Exit fullscreen mode

The JSON returns perfectly. But then, you move that logic into a Node.js application using the official @google-cloud/logging library.

import { Logging } from "@google-cloud/logging";

export async function readLogsAsync() {
  // Common approach: Initializing the client without explicit auth
  const logging = new Logging();

  const [entries, , response] = await logging.getEntries({
    filter: 'resource.type="k8s_container"',
    resourceNames: ['projects/your-project-id'],
  });

  console.log(entries.length, response.nextPageToken);
}
Enter fullscreen mode Exit fullscreen mode

All of a sudden, the code fails with a cryptic error: Error: Could not load the default credentials.

This happens because of a disconnect between how the gcloud CLI manages session tokens and how the Google Cloud client libraries search for credentials.

While your curl command relies on the explicit token you provided via gcloud auth print-access-token, the client libraries are designed to look for Application Default Credentials (ADC).

Running gcloud auth login authenticates, but it does not create the specific credential file that the Node.js library requires to run locally.

🤫 Authenticate for local development

The most efficient way to solve this is to provide the library with the credentials it is looking for. Run the gcloud auth application-default login command in your terminal.

gcloud auth application-default login
Enter fullscreen mode Exit fullscreen mode

This opens a browser window to authorize your account and saves a JSON file to your local configuration folder. Once this is done, your Node.js code will automatically find these credentials—no code changes required.

🏡 Moving to Production

Once you get your local environment running, you need to think about "productionizing" your code. Hardcoded project IDs and lack of robust error handling are common causes of production outages.

  • 🛡️ Robust Error Handling

In production, your app should handle permission issues or network timeouts gracefully.

try {
  const [entries] = await logging.getEntries({
    filter: 'resource.type="k8s_container"',
    resourceNames: [`projects/${process.env.GOOGLE_CLOUD_PROJECT}`],
    pageSize: 10, 
  });
} catch (error) {
  if (error.code === 7) {
    console.error("Permission Denied: Check your Service Account roles.");
  } else {
    console.error("Unexpected Logging Error:", error.message);
  }
}
Enter fullscreen mode Exit fullscreen mode
  • 🎭 Service Accounts

In production, you shouldn't use your personal user credentials. Use a Service Account with the "Logs Viewer" role.

  • 🌍 Environment Variables

Avoid hardcoding project IDs. Use an environment variable to make your code portable across staging and production environments.

const PROJECT_ID = process.env.GOOGLE_CLOUD_PROJECT || 'your-project-id';
...

export async function readLogsAsync() {
  const logging = new Logging({ projectId: PROJECT_ID });

Enter fullscreen mode Exit fullscreen mode
  • Structured Logging

Instead of just reading logs, think about how you write them. Using structured JSON logs makes them much easier to query later in the Cloud Logging console.

If you are running on Google Kubernetes Engine (GKE), you don't even need to use the Logging library to write logs. Printing a JSON string to stdout allows GKE to parse your data into searchable fields automatically.

export function logStructuredStatus() {
  const logEntry = {
    severity: "INFO",
    message: "Container health check successful",
    container_info: { version: "v2.1.0", uptime: process.uptime() },
    http_stats: { active_connections: 42 }
  };

  // GKE picks this up and converts it to a structured log automatically
  console.log(JSON.stringify(logEntry));
}
Enter fullscreen mode Exit fullscreen mode

Get started

I'll be sharing a more thorough walk-through on using the native observability features provided with GKE real soon—including how to automate this stuff so you can avoid the manual debugging of credential errors.

In the meantime, dive deeper here:

Top comments (0)