Episode 24 membangun observability production: logging terstruktur dengan Pino, setup Apollo Studio untuk query analytics dan schema checks, integrasi OpenTelemetry untuk distributed tracing, metrik Prometheus dan dashboard Grafana, APM seperti Sentry, hingga health checks.

API yang tidak bisa diamati sama dengan berjalan dalam kegelapan. Episode 24 membangun observability untuk GraphQL production: logging, tracing, metrik, dan health checks yang membuat kalian bisa melihat apa yang terjadi di dalam server dari luar. Kita akan membahas logging terstruktur dengan Pino, query analytics dengan Apollo Studio, distributed tracing dengan OpenTelemetry, metrik dengan Prometheus dan Grafana, APM untuk error tracking, serta health checks.
Log di production harus terstruktur — JSON dengan field konsisten — agar bisa difilter dan di-agregasi. Pino adalah pilihan populer karena cepat; install dengan npm install pino:
import pino from "pino";
export const logger = pino({
level: process.env.LOG_LEVEL ?? "info",
});Integrasikan logger ke context agar setiap resolver memakai logger yang sama — buat logger turunan per request dengan logger.child({ requestId: crypto.randomUUID() }):
const { url } = await startStandaloneServer(server, {
context: async ({ req }) => ({
logger: logger.child({ requestId: crypto.randomUUID() }),
user,
}),
});import { ApolloServerPlugin } from "@apollo/server";
const logPlugin = {
async requestDidStart() {
return {
async willSendResponse({ response, contextValue }) {
contextValue.logger.info({ body: response.body }, "response dikirim");
},
};
},
};Apollo Studio adalah platform observability Apollo yang menampilkan operasi GraphQL secara langsung. Setelah mendaftarkan graph dan mengisi key:
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [ApolloServerPluginUsageReporting({ sendErrors: { unmodified: true } })],
});Dalam arsitektur microservices, sebuah query melewati banyak service. OpenTelemetry menstandardisasi tracing agar perjalanan satu request bisa dilacak lintas service; install dengan npm install @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node:
import { NodeSDK } from "@opentelemetry/sdk-node";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
const sdk = new NodeSDK({
traceExporter: { url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT },
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();Konsep intinya: setiap request membawa trace id, dipecah menjadi span (satu per resolver atau per query database), dan context melewati propagation agar service penerima tetap dalam trace yang sama. Ini membuat bottleneck antar-service terlihat jelas.
Untuk metrik, ekspos metrik Prometheus dari server; install dengan npm install prom-client:
import { Counter, Histogram } from "prom-client";
const queriesTotal = new Counter({
name: "graphql_queries_total",
help: "Jumlah query",
labelNames: ["operationName"],
});
const queryDuration = new Histogram({
name: "graphql_query_duration_seconds",
help: "Durasi query",
labelNames: ["operationName"],
});Prometheus menarik metrik dari endpoint /metrics, dan Grafana menampilkannya sebagai dashboard. Metrik yang wajib ada: total query per operasi, histogram durasi, jumlah error, dan kesehatan database.
Untuk error tracking, integrasikan Sentry; install dengan npm install @sentry/node:
import * as Sentry from "@sentry/node";
Sentry.init({ dsn: process.env.SENTRY_DSN });
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [
{
async requestDidStart() {
return {
async didEncounterErrors({ errors }) {
errors.forEach(Sentry.captureException);
},
};
},
},
],
});Sentry menampilkan error beserta stack trace, user, dan konteks request — menggantikan kebiasaan "melihat log di server". Alternatif lain: Datadog dan New Relic untuk APM terintegrasi.
Health checks memberi tahu orchestrator (Kubernetes, load balancer) apakah aplikasi sehat:
app.get("/health/live", (req, res) => {
res.json({ status: "ok" });
});
app.get("/health/ready", async (req, res) => {
try {
await prisma.$queryRaw`SELECT 1`;
res.json({ status: "ok" });
} catch {
res.status(503).json({ status: "degraded" });
}
});Readiness check menguji dependency (database, Redis) sehingga load balancer tahu kapan aplikasi sebenarnya siap. Ini wajib dipasang sebelum deployment otomatis — kita pakai lagi di episode 31 dan 35.
Inti yang harus dibawa pulang:
requestId memudahkan pelacakan.Di episode 25 selanjutnya kalian akan mempelajari Apollo Client fundamentals — setup client dan ApolloProvider, hook useQuery dengan polling dan refetch, useMutation dengan optimistic UI, manajemen cache InMemoryCache, hingga local state dengan reactive variables. Sisi frontend akan tersambung penuh ke GraphQL!