refactor: migrate to nextjs serverless handler for vercel

This commit is contained in:
MelvynFaith 2026-09-09 17:12:18 +07:00
parent 4dc4a1f716
commit 6edf45b5f4
3 changed files with 1111 additions and 4 deletions

1008
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,8 +4,9 @@
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "dev": "next dev",
"start": "node index.js" "build": "next build",
"start": "next start"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",
@ -13,9 +14,13 @@
"type": "commonjs", "type": "commonjs",
"dependencies": { "dependencies": {
"@apollo/server": "^5.5.1", "@apollo/server": "^5.5.1",
"@as-integrations/next": "^4.1.0",
"cors": "^2.8.6", "cors": "^2.8.6",
"dotenv": "^17.4.2", "dotenv": "^17.4.2",
"graphql": "^16.14.2", "graphql": "^16.14.2",
"pg": "^8.23.0" "next": "^16.3.4",
"pg": "^8.23.0",
"react": "^19.2.8",
"react-dom": "^19.2.8"
} }
} }

96
pages/api/graphql.js Normal file
View File

@ -0,0 +1,96 @@
const { ApolloServer } = require('@apollo/server');
const { startServerAndCreateNextHandler } = require('@as-integrations/next');
const { Pool } = require('pg');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: false,
},
max: 1, // Menjaga batas koneksi pada lingkungan serverless
});
const typeDefs = `#graphql
type Category {
id: ID!
name: String!
description: String
products: [Product!]!
}
type Product {
id: ID!
title: String!
price: Float!
category_id: Int
created_by: ID
category: Category
}
type Query {
categories: [Category!]!
category(id: ID!): Category
products: [Product!]!
product(id: ID!): Product
}
`;
const resolvers = {
Query: {
categories: async () => {
const result = await pool.query('SELECT * FROM categories ORDER BY id ASC');
return result.rows;
},
category: async (_, { id }) => {
const result = await pool.query('SELECT * FROM categories WHERE id = $1', [id]);
return result.rows[0] || null;
},
products: async () => {
const result = await pool.query('SELECT id, title, price::float, category_id, created_by FROM products ORDER BY id ASC');
return result.rows;
},
product: async (_, { id }) => {
const result = await pool.query('SELECT id, title, price::float, category_id, created_by FROM products WHERE id = $1', [id]);
return result.rows[0] || null;
},
},
Category: {
products: async (parent) => {
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]
);
return result.rows;
},
},
Product: {
category: async (parent) => {
if (!parent.category_id) return null;
const result = await pool.query('SELECT * FROM categories WHERE id = $1', [parent.category_id]);
return result.rows[0] || null;
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
introspection: true,
});
const apolloHandler = startServerAndCreateNextHandler(server);
// Wrapper untuk menangani CORS Apollo Studio Sandbox
module.exports = async function handler(req, res) {
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Origin', 'https://studio.apollographql.com');
res.setHeader('Access-Control-Allow-Methods', 'GET,OPTIONS,POST');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
res.status(200).end();
return;
}
return apolloHandler(req, res);
};