diff --git a/.gitignore b/.gitignore index d88da03..2754534 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ node_modules .vercel AGENTS.md CLAUDE.md -.next \ No newline at end of file +.next \ No newline at end of file diff --git a/pages/api/graphql.js b/pages/api/graphql.js index a301bdd..6d6d071 100644 --- a/pages/api/graphql.js +++ b/pages/api/graphql.js @@ -8,10 +8,10 @@ const pool = new Pool({ ssl: { rejectUnauthorized: false, }, - max: 1, // Batasi 1 koneksi per instance serverless + max: 1, }); -// 2. Definisi Schema GraphQL (typeDefs) +// 2. Definisi Schema GraphQL const typeDefs = `#graphql type Category { id: ID! @@ -37,7 +37,26 @@ const typeDefs = `#graphql } `; -// 3. Definisi Resolver (Query + Nested Resolvers) +// 3. Plugin Apollo untuk menyuntikkan log counter ke response extensions +const metricsPlugin = { + async requestDidStart() { + return { + async willSendResponse({ response, contextValue }) { + if (response.body.kind === 'single') { + response.body.singleResult.extensions = { + ...response.body.singleResult.extensions, + nPlusOneMetrics: { + totalCalls: contextValue.metrics.categoryProductsCalls, + resolverLogs: contextValue.metrics.callLogs, + }, + }; + } + }, + }; + }, +}; + +// 4. Definisi Resolver const resolvers = { Query: { categories: async () => { @@ -65,7 +84,16 @@ const resolvers = { // Resolver Relasi Category -> Products (One-to-Many) Category: { - products: async (parent) => { + products: async (parent, _, contextValue) => { + // Increment counter di context request saat ini + contextValue.metrics.categoryProductsCalls += 1; + const currentCall = contextValue.metrics.categoryProductsCalls; + + // Simpan log urutan pemanggilan ke array context + contextValue.metrics.callLogs.push( + `[resolver counter] Category.products dipanggil ${currentCall} kali (category_id=${parent.id})` + ); + const result = await pool.query( 'SELECT id, title, price::float, category_id, created_by FROM products WHERE category_id = $1 ORDER BY id ASC', [parent.id] @@ -84,17 +112,26 @@ const resolvers = { }, }; -// 4. Inisialisasi Apollo Server +// 5. Inisialisasi Apollo Server dengan Plugin const server = new ApolloServer({ typeDefs, resolvers, introspection: true, - csrfPrevention: false, // Mematikan proteksi CSRF agar request dari browser Sandbox tidak dicegat + csrfPrevention: false, + plugins: [metricsPlugin], }); -const apolloHandler = startServerAndCreateNextHandler(server); +// 6. Inisialisasi Handler dengan Context Per-Request +const apolloHandler = startServerAndCreateNextHandler(server, { + context: async () => ({ + metrics: { + categoryProductsCalls: 0, + callLogs: [], + }, + }), +}); -// 5. Handler CORS untuk Apollo Studio dan Embedded Sandbox +// 7. Handler CORS dan Serverless Entrypoint module.exports = async function handler(req, res) { const allowedOrigins = new Set([ 'https://studio.apollographql.com', @@ -113,11 +150,10 @@ module.exports = async function handler(req, res) { 'Content-Type, Authorization, apollo-require-preflight, x-apollo-operation-name' ); - // Respons langsung untuk preflight OPTIONS request if (req.method === 'OPTIONS') { res.status(200).end(); return; } return apolloHandler(req, res); -}; +}; \ No newline at end of file