Introduction
Google Cloud Key Management Service (KMS) is a managed service for creating, managing, and using cryptographic keys to encrypt data at rest or in transit. In 2026, with growing regulations like GDPR and zero-trust requirements, KMS is essential for cloud-native applications.
This intermediate tutorial walks you through implementing KMS in a Node.js app: from key creation to integration in a Next.js API. You'll learn to encrypt/decrypt sensitive data like tokens or configs using the official client API. Every step includes complete, functional code tested locally with Application Default Credentials (ADC).
Why it matters: KMS handles automatic key rotation, audits via Cloud Audit Logs, and native integration with Cloud Storage, BigQuery, or Compute Engine. By the end, you'll have a secure REST API ready for production. Estimated time: 30 minutes.
Prerequisites
- Active Google Cloud Platform (GCP) account with billing enabled.
- gcloud CLI installed and configured (
gcloud init). - Node.js 20+ and npm/yarn.
- Next.js 15+ project (created via
npx create-next-app@latestwith App Router). - Basic knowledge of TypeScript and cryptography (symmetric/asymmetric).
Install Dependencies
npm init -y
npm install @google-cloud/kms typescript ts-node @types/node
npm install -D next@latest react react-dom @types/react @types/react-dom
# Pour un projet Next.js complet (optionnel mais recommandé)
npx create-next-app@latest mon-app-kms --typescript --app --tailwind --eslint --src-dir --import-alias "@/*"
cd mon-app-kms
npm install @google-cloud/kmsThis script initializes a Node.js or Next.js project and installs the official @google-cloud/kms client. ts-node lets you run TypeScript directly. For Next.js, it creates a boilerplate project with App Router for later API integration. Verify with npm ls @google-cloud/kms.
Set Up GCP Authentication
KMS requires secure authentication. Use Application Default Credentials (ADC) for smooth local development: they prioritize service account or gcloud user credentials. Enable the KMS API in your GCP project via console.cloud.google.com/apis/library/kms.googleapis.com.
Authenticate and Enable the API
gcloud auth application-default login
gcloud services enable cloudkms.googleapis.com --project=VOTRE_PROJECT_ID
gcloud kms locations list --project=VOTRE_PROJECT_IDThe gcloud auth application-default login command sets up ADC for the Node.js client. Replace VOTRE_PROJECT_ID with your GCP project ID (e.g., my-project-123). locations list checks availability (use 'global' or 'europe'). Avoid static API keys in production; prefer service accounts.
Create a Key Ring and Key
export PROJECT_ID=VOTRE_PROJECT_ID
export LOCATION=global
export KEYRING_NAME=my-keyring
export KEY_NAME=my-key
gcloud kms keyrings create $KEYRING_NAME \
--location=$LOCATION \
--project=$PROJECT_ID
gcloud kms keys create $KEY_NAME \
--location=$LOCATION \
--keyring=$KEYRING_NAME \
--purpose=encryption \
--project=$PROJECT_ID
gcloud kms keys list --keyring=$KEYRING_NAME --location=$LOCATION --project=$PROJECT_IDThis creates a key ring (logical container) and a symmetric key for encryption (purpose=encryption). list confirms creation. Use 'global' for simplicity, or 'europe' for EU compliance. Keys are HSM-backed by default in 2026, resistant to extraction.
Encrypt and Decrypt Data
Now for the client code. The KMS client wraps plaintext into base64 ciphertext, which you can store in a database. Imagine encrypting a JWT token before storing it in Cloud SQL.
TypeScript Encryption Script
import {KeyManagementServiceClient} from '@google-cloud/kms';
const client = new KeyManagementServiceClient();
const projectId = 'VOTRE_PROJECT_ID';
const locationId = 'global';
const keyRingId = 'my-keyring';
const keyId = 'my-key';
const name = client.cryptoKeyPath(projectId, locationId, keyRingId, keyId);
async function encrypt(plaintext: string): Promise<string> {
const [result] = await client.encrypt({
name,
plaintext: Buffer.from(plaintext),
});
return result.ciphertext!.toString('base64');
}
(async () => {
const encrypted = await encrypt('Mon secret super sensible');
console.log('Ciphertext:', encrypted);
})();This script creates a KMS client, references the key via an ARN-like path, and encrypts plaintext (as Buffer) to base64 ciphertext. Run with npx ts-node kms-encrypt.ts. Replace the IDs. Pitfall: Always use Buffer to avoid UTF-8 encoding issues.
TypeScript Decryption Script
import {KeyManagementServiceClient} from '@google-cloud/kms';
const client = new KeyManagementServiceClient();
const projectId = 'VOTRE_PROJECT_ID';
const locationId = 'global';
const keyRingId = 'my-keyring';
const keyId = 'my-key';
const name = client.cryptoKeyPath(projectId, locationId, keyRingId, keyId);
async function decrypt(ciphertext: string): Promise<string> {
const [result] = await client.decrypt({
name,
ciphertext: Buffer.from(ciphertext, 'base64'),
});
return result.plaintext!.toString();
}
(async () => {
const ciphertext = 'CiQA...'; // Remplacez par output de encrypt
const decrypted = await decrypt(ciphertext);
console.log('Plaintext:', decrypted);
})();Symmetric to encryption, this code decrypts base64 ciphertext back to plaintext. Note Buffer.from(..., 'base64'). Test by copying the output from encrypt.ts. Common error: Key protections (e.g., rotation) disable decrypt on old versions.
Integrate KMS into a Next.js API
Advantage: Next.js App Router serverless functions integrate seamlessly with KMS via ADC in dev, or Workload Identity in production on Cloud Run/Vercel.
Encryption API Route (Next.js)
import {KeyManagementServiceClient} from '@google-cloud/kms';
import {NextRequest, NextResponse} from 'next/server';
const client = new KeyManagementServiceClient();
const projectId = process.env.GCP_PROJECT_ID || 'VOTRE_PROJECT_ID';
const locationId = 'global';
const keyRingId = 'my-keyring';
const keyId = 'my-key';
const name = client.cryptoKeyPath(projectId!, locationId, keyRingId, keyId);
export async function POST(req: NextRequest) {
try {
const {plaintext} = await req.json();
if (!plaintext) {
return NextResponse.json({error: 'Plaintext requis'}, {status: 400});
}
const [result] = await client.encrypt({name, plaintext: Buffer.from(plaintext)});
return NextResponse.json({ciphertext: result.ciphertext!.toString('base64')});
} catch (error) {
return NextResponse.json({error: 'Erreur chiffrement'}, {status: 500});
}
}This POST /api/encrypt route receives JSON {plaintext}, encrypts it via KMS, and returns ciphertext. Use an env var for projectId. Test with curl or Postman. Secure in production with API keys or IAM.
Decryption API Route (Next.js)
import {KeyManagementServiceClient} from '@google-cloud/kms';
import {NextRequest, NextResponse} from 'next/server';
const client = new KeyManagementServiceClient();
const projectId = process.env.GCP_PROJECT_ID || 'VOTRE_PROJECT_ID';
const locationId = 'global';
const keyRingId = 'my-keyring';
const keyId = 'my-key';
const name = client.cryptoKeyPath(projectId!, locationId, keyRingId, keyId);
export async function POST(req: NextRequest) {
try {
const {ciphertext} = await req.json();
if (!ciphertext) {
return NextResponse.json({error: 'Ciphertext requis'}, {status: 400});
}
const [result] = await client.decrypt({name, ciphertext: Buffer.from(ciphertext, 'base64')});
return NextResponse.json({plaintext: result.plaintext!.toString()});
} catch (error) {
return NextResponse.json({error: 'Erreur déchiffrement'}, {status: 500});
}
}Mirror decryption route POST /api/decrypt with input validation. Run npm run dev and test at localhost:3000/api/decrypt. Pitfall: Without try/catch, IAM errors (e.g., kms.keys.decrypt permission) crash the app.
Best Practices
- Automatic Rotation: Enable with
gcloud kms keys update --rotation-period=90dto renew keys without downtime. - Minimal Permissions: Use roles like
roles/cloudkms.cryptoKeyEncrypterDecrypteron service accounts. - Ciphertext Storage: Base64 in DB (Cloud SQL/PostgreSQL); limit size (4KB max per op).
- Audits: Enable Cloud Audit Logs to track all KMS access.
- Multi-Region: For HA, duplicate key rings in 'europe-west1' and 'us-central1'.
Common Errors to Avoid
- Wrong Region: Key in 'global' inaccessible from 'europe' without replicas.
- Missing ADC: 401 error; rerun
gcloud auth application-default login. - Plaintext Too Large: KMS limits 64KB; chunk large files with hybrid envelope.
- No Validation: Always sanitize inputs to prevent injections in plaintext.
Next Steps
- Official docs: Google Cloud KMS.
- Advanced: Hybrid Encryption for Large Volumes.
- Learni Group Training on GCP Security and DevOps.
- Integrate with Cloud HSM for FIPS 140-2 Level 3.