feat: setup graphql server with neon relation resolvers
This commit is contained in:
commit
4dc4a1f716
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
.env
|
||||
99
index.js
Normal file
99
index.js
Normal file
@ -0,0 +1,99 @@
|
||||
const { ApolloServer } = require('@apollo/server');
|
||||
const { startStandaloneServer } = require('@apollo/server/standalone');
|
||||
const { Pool } = require('pg');
|
||||
require('dotenv').config();
|
||||
|
||||
// 1. Inisialisasi Pool PostgreSQL
|
||||
const pool = new Pool({
|
||||
connectionString: process.env.DATABASE_URL,
|
||||
ssl: {
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
});
|
||||
|
||||
// 2. Definisi Schema GraphQL (typeDefs)
|
||||
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
|
||||
}
|
||||
`;
|
||||
|
||||
// 3. Definisi Resolver (Query + Nested Resolvers)
|
||||
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 () => {
|
||||
// price di-cast ke float agar cocok dengan tipe Float di GraphQL
|
||||
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;
|
||||
},
|
||||
},
|
||||
|
||||
// Resolver Relasi Category -> Products (One-to-Many)
|
||||
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;
|
||||
},
|
||||
},
|
||||
|
||||
// Resolver Relasi Product -> Category (Many-to-One)
|
||||
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;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// 4. Inisialisasi Apollo Server
|
||||
const server = new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
introspection: true, // Wajib aktif untuk Apollo Sandbox publik
|
||||
});
|
||||
|
||||
// 5. Jalankan Server
|
||||
async function startServer() {
|
||||
const port = process.env.PORT || 4000;
|
||||
const { url } = await startStandaloneServer(server, {
|
||||
listen: { port: Number(port) },
|
||||
});
|
||||
console.log(`Server GraphQL siap di: ${url}`);
|
||||
}
|
||||
|
||||
startServer();
|
||||
1527
package-lock.json
generated
Normal file
1527
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
package.json
Normal file
21
package.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "week-4",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "node index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"@apollo/server": "^5.5.1",
|
||||
"cors": "^2.8.6",
|
||||
"dotenv": "^17.4.2",
|
||||
"graphql": "^16.14.2",
|
||||
"pg": "^8.23.0"
|
||||
}
|
||||
}
|
||||
24
test-db.js
Normal file
24
test-db.js
Normal file
@ -0,0 +1,24 @@
|
||||
const { Pool } = require('pg');
|
||||
require('dotenv').config();
|
||||
|
||||
const pool = new Pool({
|
||||
connectionString: process.env.DATABASE_URL,
|
||||
ssl: {
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
});
|
||||
|
||||
async function test() {
|
||||
try {
|
||||
const res = await pool.query('SELECT NOW() AS server_time, COUNT(*) AS total_products FROM products');
|
||||
console.log('Koneksi Sukses!');
|
||||
console.log('Waktu DB Neon:', res.rows[0].server_time);
|
||||
console.log('Jumlah Data Produk:', res.rows[0].total_products);
|
||||
} catch (err) {
|
||||
console.error('Koneksi Gagal:', err.message);
|
||||
} finally {
|
||||
await pool.end();
|
||||
}
|
||||
}
|
||||
|
||||
test();
|
||||
Loading…
x
Reference in New Issue
Block a user