Node.js’te Apollo Server ve Sequelize ile GraphQL Sunucusu Kurulumu
Giriş
Modern web uygulamaları, verilerle etkileşim kurmak için güçlü ve esnek API’lere ihtiyaç duyar. Geleneksel REST API’leri belirli senaryolarda etkili olsa da, veri ihtiyacının karmaşıklaştığı, istemcilerin farklı veri setleri talep ettiği veya aşırı/eksik veri sorunlarının yaşandığı durumlarda GraphQL, daha verimli bir alternatif sunar. Bu makale, Node.js ortamında GraphQL sunucusu kurmak için endüstri standardı Apollo Server’ı ve ilişkisel veritabanı yönetimini kolaylaştıran güçlü ORM (Object-Relational Mapper) Sequelize’ı nasıl entegre edeceğinizi adım adım açıklayacaktır.
GraphQL Nedir?
GraphQL, Facebook tarafından geliştirilen ve 2015 yılında açık kaynak olarak yayınlanan, API’ler için bir sorgu dilidir. Temel amacı, istemcilerin ihtiyaç duydukları veriyi tam olarak talep etmelerini sağlayarak, sunucudan gereksiz veri çekme veya birden fazla istek yapma sorunlarını ortadan kaldırmaktır. GraphQL’in başlıca avantajları şunlardır:
- Verimlilik: İstemciler tam olarak neye ihtiyaç duyduklarını belirtir, bu da ağ trafiğini azaltır ve performansı artırır.
- Tek Uç Nokta: Tüm sorgular tek bir HTTP POST uç noktasına yapılır.
- Güçlü Tip Sistemi: Şema tabanlı yapısı sayesinde API’nin sunduğu tüm veri tipleri ve ilişkileri önceden tanımlanır, bu da geliştirme sürecini kolaylaştırır ve hata olasılığını azaltır.
- Geliştirici Deneyimi: Şema keşfedilebilirliği ve otomatik tamamlama özellikleri sayesinde geliştiriciler için daha iyi bir deneyim sunar.
Apollo Server Nedir?
Apollo Server, GraphQL API’leri oluşturmak için kullanılan açık kaynaklı, üretim ortamına hazır bir GraphQL sunucusudur. Node.js üzerinde çalışır ve GraphQL şemanızı ve çözümleyicilerinizi (resolvers) kolayca entegre etmenizi sağlar. Express, Koa gibi çeşitli Node.js HTTP framework’leri ile uyumludur ve geliştirme sürecini hızlandıran güçlü araçlar (Apollo Sandbox/GraphQL Playground) sunar.
Sequelize Nedir?
Sequelize, Node.js için Promise tabanlı bir ORM’dir. PostgreSQL, MySQL, MariaDB, SQLite ve MSSQL gibi çeşitli ilişkisel veritabanlarını destekler. Sequelize, JavaScript nesnelerini veritabanı tablolarına eşleyerek SQL sorguları yazma ihtiyacını büyük ölçüde azaltır. Modeller, ilişkiler, migrasyonlar ve seed’ler gibi özellikleriyle veritabanı yönetimini basitleştirir ve daha tutarlı hale getirir.
Bu makalede, bir Node.js projesinde Apollo Server’ı kurarak GraphQL şemamızı tanımlayacak, Sequelize ile veritabanı modellerimizi oluşturacak ve bu iki teknolojiyi birleştirerek güçlü bir GraphQL API’si geliştireceğiz.
Ön Gereksinimler
Bu rehberi takip edebilmek için aşağıdaki ön gereksinimlere sahip olmanız gerekmektedir:
- Node.js ve npm/yarn: Bilgisayarınızda Node.js ve npm (Node Package Manager) veya yarn kurulu olmalıdır. Node.js’in resmi web sitesinden indirebilirsiniz.
- Temel JavaScript/TypeScript Bilgisi: ES6+ JavaScript sözdizimine aşina olmanız beklenir.
- Veritabanı Bilgisi: İlişkisel veritabanları (PostgreSQL, MySQL, SQLite vb.) hakkında temel bilgiye sahip olmak faydalı olacaktır. Bu rehberde SQLite’ı kullanacağız, ancak diğer veritabanlarına geçiş yapmak kolaydır.
Proje Yapılandırması ve Temel Kurulum
İlk adım olarak, yeni bir Node.js projesi oluşturup gerekli bağımlılıkları yükleyeceğiz.
Proje Başlatma
Yeni bir dizin oluşturun ve içine girin:
mkdir graphql-sequelize-app
cd graphql-sequelize-app
Projenizi başlatın:
npm init -y
Bu komut, varsayılan ayarlar ile bir package.json dosyası oluşturacaktır.
Gerekli Paketlerin Yüklenmesi
Şimdi projemiz için gerekli olan temel paketleri yükleyelim:
npm install apollo-server graphql sequelize sqlite3 dotenv nodemon
apollo-server: GraphQL sunucusunu kurmak için ana paket.graphql: GraphQL şemaları ve sorguları için temel kütüphane.sequelize: ORM kütüphanesi.sqlite3: SQLite veritabanı sürücüsü. (Eğer PostgreSQL kullanacaksanızpg, MySQL içinmysql2kurmalısınız).dotenv: Ortam değişkenlerini.envdosyasından yüklemek için.nodemon: Geliştirme sırasında dosya değişikliklerini algılayıp sunucuyu otomatik olarak yeniden başlatmak için.
package.json dosyanıza start ve dev script’lerini ekleyelim:
// package.json
{
"name": "graphql-sequelize-app",
"version": "1.0.0",
"description": "",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js",
"dev": "nodemon src/index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"apollo-server": "^3.13.0",
"dotenv": "^16.4.5",
"graphql": "^16.8.1",
"sequelize": "^6.37.3",
"sqlite3": "^5.1.7"
},
"devDependencies": {
"nodemon": "^3.1.0"
}
}
Proje Klasör Yapısı
Organize bir proje için aşağıdaki gibi bir klasör yapısı önerilir:
graphql-sequelize-app/
├── src/
│ ├── index.js // Ana sunucu dosyası
│ ├── graphql/
│ │ ├── schema.js // GraphQL şema tanımları
│ │ └── resolvers.js // GraphQL çözümleyicileri
│ └── models/ // Sequelize modelleri
│ ├── index.js // Sequelize bağlantısı ve model ilişkileri
│ ├── user.js
│ └── post.js
├── config/
│ └── config.json // Sequelize CLI yapılandırması
├── .env // Ortam değişkenleri
├── package.json
└── ...
Bu yapı, GraphQL şemalarını, çözümleyicileri ve Sequelize modellerini ayrı tutarak projenin ölçeklenebilirliğini ve bakımını kolaylaştırır.
Veritabanı Yapılandırması ve Sequelize Entegrasyonu
Şimdi Sequelize’ı projemize entegre edelim ve veritabanı bağlantısını kuralım.
Ortam Değişkenleri (.env)
Veritabanı bağlantı bilgilerini güvenli bir şekilde yönetmek için .env dosyasını kullanacağız. Projenizin kök dizininde bir .env dosyası oluşturun:
# .env
DATABASE_URL=sqlite::memory: # Geliştirme için geçici bellek veritabanı veya
DATABASE_URL=sqlite:./database.sqlite # Kalıcı dosya tabanlı veritabanı
sqlite::memory: geliştirme sırasında hızlı testler için idealdir, ancak sunucu her yeniden başlatıldığında veriler kaybolur. Kalıcı bir veritabanı için sqlite:./database.sqlite kullanabilirsiniz.
Sequelize CLI Kurulumu ve Yapılandırması
Sequelize CLI, model, migrasyon ve seed dosyalarını oluşturmak için kullanışlı bir araçtır. Geliştirme bağımlılığı olarak yükleyin:
npm install --save-dev sequelize-cli
Şimdi Sequelize CLI’ı başlatın:
npx sequelize-cli init
Bu komut, config, models, migrations ve seeders dizinlerini oluşturacaktır. config/config.json dosyasını düzenleyerek veritabanı bağlantı bilgilerimizi ortam değişkenlerinden alacak şekilde ayarlayalım:
// config/config.json
{
"development": {
"dialect": "sqlite",
"storage": "./database.sqlite",
"logging": false
},
"test": {
"dialect": "sqlite",
"storage": ":memory:"
},
"production": {
"dialect": "sqlite",
"storage": "./database.sqlite"
}
}
Yukarıdaki yapılandırma, sqlite kullanıldığında database.sqlite dosyasını veritabanı olarak kullanır. Eğer PostgreSQL veya MySQL kullanacaksanız, dialect ve bağlantı bilgilerini buna göre güncellemeli ve use_env_variable özelliğini kullanmalısınız. Örneğin:
// config/config.json (PostgreSQL/MySQL için örnek)
{
"development": {
"use_env_variable": "DATABASE_URL",
"dialect": "postgres", // veya "mysql"
"logging": false
},
// ... diğer ortamlar
}
Sequelize Bağlantısı ve Model Tanımlama
src/models/index.js dosyası, tüm Sequelize modellerinizi bir araya getirecek ve veritabanı bağlantısını yönetecek ana dosyadır.
// src/models/index.js
require('dotenv').config();
const { Sequelize, DataTypes } = require('sequelize');
const UserModel = require('./user');
const PostModel = require('./post');
const env = process.env.NODE_ENV || 'development';
const config = require('../../config/config.json')[env];
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
const db = {};
db.sequelize = sequelize;
db.Sequelize = Sequelize;
// Modelleri yükle
db.User = UserModel(sequelize, DataTypes);
db.Post = PostModel(sequelize, DataTypes);
// İlişkileri tanımla
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
module.exports = db;
Model Oluşturma
İki basit model oluşturalım: User (Kullanıcı) ve Post (Gönderi). Bir kullanıcı birden fazla gönderi yazabilir.
User Modeli
src/models/user.js dosyasını oluşturun:
// src/models/user.js
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
username: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
email: {
type: DataTypes.STRING,
unique: true,
allowNull: false,
validate: {
isEmail: true
}
}
});
User.associate = (models) => {
User.hasMany(models.Post, {
foreignKey: 'userId',
as: 'posts'
});
};
return User;
};
Post Modeli
src/models/post.js dosyasını oluşturun:
// src/models/post.js
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define('Post', {
title: {
type: DataTypes.STRING,
allowNull: false
},
content: {
type: DataTypes.TEXT,
allowNull: false
}
});
Post.associate = (models) => {
Post.belongsTo(models.User, {
foreignKey: 'userId',
as: 'author'
});
};
return Post;
};
Veritabanı Migrasyonları
Sequelize CLI ile modellerimiz için migrasyon dosyaları oluşturalım:
npx sequelize-cli model:generate --name User --attributes username:string,email:string
npx sequelize-cli model:generate --name Post --attributes title:string,content:text,userId:integer
Bu komutlar, migrations klasöründe iki yeni dosya oluşturacaktır. userId alanının Post modelinde yabancı anahtar olarak tanımlandığına dikkat edin. Migrasyon dosyalarını inceleyip gerekirse düzenleyebilirsiniz (örneğin unique veya allowNull kısıtlamaları eklemek için).
Şimdi migrasyonları çalıştırarak veritabanı tablolarını oluşturalım:
npx sequelize-cli db:migrate
Bu komut, database.sqlite (veya yapılandırdığınız başka bir veritabanı) içinde Users ve Posts tablolarını oluşturacaktır.
GraphQL Şema Tanımlaması
GraphQL şemanız, istemcilerin sorgulayabileceği veya değiştirebileceği verilerin yapısını tanımlar. src/graphql/schema.js dosyasını oluşturalım:
// src/graphql/schema.js
const { gql } = require('apollo-server');
const typeDefs = gql
type User {
id: ID!
username: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
}
input CreateUserInput {
username: String!
email: String!
}
input UpdateUserInput {
username: String
email: String
}
input CreatePostInput {
title: String!
content: String!
userId: ID!
}
input UpdatePostInput {
title: String
content: String
}
type Query {
users: [User!]!
user(id: ID!): User
posts: [Post!]!
post(id: ID!): Post
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
deleteUser(id: ID!): Boolean!
createPost(input: CreatePostInput!): Post!
updatePost(id: ID!, input: UpdatePostInput!): Post!
deletePost(id: ID!): Boolean!
}
;
module.exports = typeDefs;
Şema Açıklaması
UservePosttipleri, veritabanındaki karşılık gelen modellerin alanlarını tanımlar.ID!gibi ünlem işaretleri, alanın zorunlu olduğunu belirtir.postsalanıUsertipinde,authoralanıPosttipinde, modeller arasındaki ilişkileri yansıtır.inputtipleri,Mutationişlemleri için girdi verilerini tanımlar. Bu, daha temiz ve yeniden kullanılabilir bir yapı sağlar.Querytipi, istemcilerin veri okuma (GET) işlemleri için kullanabileceği sorguları tanımlar. Örneğin, tüm kullanıcıları (users) veya belirli bir kullanıcıyı (user(id:)) getirme.Mutationtipi, istemcilerin veri oluşturma, güncelleme ve silme (POST, PUT, DELETE) işlemleri için kullanabileceği mutasyonları tanımlar.
GraphQL Çözümleyicileri (Resolvers) Oluşturma
Çözümleyiciler, GraphQL şemasındaki her alanın nasıl veri sağlayacağını belirleyen fonksiyonlardır. src/graphql/resolvers.js dosyasını oluşturalım:
// src/graphql/resolvers.js
const resolvers = {
Query: {
users: async (parent, args, { db }) => {
return db.User.findAll({ include: [{ model: db.Post, as: 'posts' }] });
},
user: async (parent, { id }, { db }) => {
return db.User.findByPk(id, { include: [{ model: db.Post, as: 'posts' }] });
},
posts: async (parent, args, { db }) => {
return db.Post.findAll({ include: [{ model: db.User, as: 'author' }] });
},
post: async (parent, { id }, { db }) => {
return db.Post.findByPk(id, { include: [{ model: db.User, as: 'author' }] });
},
},
Mutation: {
createUser: async (parent, { input }, { db }) => {
return db.User.create(input);
},
updateUser: async (parent, { id, input }, { db }) => {
const user = await db.User.findByPk(id);
if (!user) throw new Error('User not found');
await user.update(input);
return user;
},
deleteUser: async (parent, { id }, { db }) => {
const user = await db.User.findByPk(id);
if (!user) throw new Error('User not found');
await user.destroy();
return true;
},
createPost: async (parent, { input }, { db }) => {
const { userId, ...postData } = input;
const user = await db.User.findByPk(userId);
if (!user) throw new Error('User not found');
return db.Post.create({ ...postData, userId });
},
updatePost: async (parent, { id, input }, { db }) => {
const post = await db.Post.findByPk(id);
if (!post) throw new Error('Post not found');
await post.update(input);
return post;
},
deletePost: async (parent, { id }, { db }) => {
const post = await db.Post.findByPk(id);
if (!post) throw new Error('Post not found');
await post.destroy();
return true;
},
},
User: {
posts: async (parent, args, { db }) => {
// Bu, parent.getPosts() ile aynı işi yapar ama daha genel bir yaklaşımdır.
// N+1 problemini çözmek için DataLoader kullanılması önerilir.
return db.Post.findAll({ where: { userId: parent.id } });
}
},
Post: {
author: async (parent, args, { db }) => {
return db.User.findByPk(parent.userId);
}
}
};
module.exports = resolvers;
Çözümleyici Açıklaması
- Her çözümleyici fonksiyonu
(parent, args, context, info)dört argüman alır. Buradaparentveargskullanılıyor.contextobjesi, veritabanı bağlantısı gibi paylaşılan kaynakları içerir. Queryçözümleyicileri,db.User.findAll(),db.User.findByPk()gibi Sequelize metodlarını kullanarak veritabanından veri çeker.includeseçeneği, ilişkili verileri (örneğin bir kullanıcıya ait gönderileri) tek bir sorguda getirmeyi sağlar.Mutationçözümleyicileri,db.User.create(),db.User.update(),db.User.destroy()gibi Sequelize metodlarını kullanarak veritabanında değişiklik yapar.UservePosttipleri altındaki çözümleyiciler, ilişkisel verileri yüklemek için kullanılır. Örneğin, birUsersorgulandığında,postsalanı ayrıca çözümlenir ve ilgili gönderileri getirir. Burada N+1 sorgu problemi oluşabilir; bu, “Gelişmiş Konular” bölümünde ele alınacaktır.
Apollo Server Kurulumu ve Başlatılması
Şimdi tüm parçaları bir araya getirerek Apollo Server’ımızı başlatalım. src/index.js dosyasını oluşturalım:
// src/index.js
require('dotenv').config();
const { ApolloServer } = require('apollo-server');
const typeDefs = require('./graphql/schema');
const resolvers = require('./graphql/resolvers');
const db = require('./models'); // Sequelize bağlantısı
async function startApolloServer() {
await db.sequelize.sync({ alter: true }); // Veritabanı tablolarını senkronize et (dev ortamı için)
const server = new ApolloServer({
typeDefs,
resolvers,
context: () => ({ db }), // Sequelize modellerini çözümleyicilere aktar
formatError: (error) => {
// Hata mesajlarını özelleştirme veya loglama
console.error(error);
return error;
},
});
const { url } = await server.listen({ port: process.env.PORT || 4000 });
console.log( Server ready at ${url});
}
startApolloServer();
Açıklama
dotenv.config()ile.envdosyasındaki ortam değişkenleri yüklenir.db.sequelize.sync({ alter: true }): Bu, Sequelize’ın veritabanı tablolarını tanımlanan modellere göre oluşturmasını veya mevcut tabloları güncellemesini sağlar. Geliştirme ortamında kullanışlıdır ancak üretim ortamında migrasyonlar tercih edilir.ApolloServerörneğitypeDefs(şema) veresolversile oluşturulur.contextfonksiyonu çok önemlidir. Bu fonksiyonun döndürdüğü obje, her bir çözümleyiciye üçüncü argüman olarak iletilir. Buradadbobjemizi (Sequelize modellerimizi içeren)context‘e ekleyerek çözümleyicilerin veritabanına erişmesini sağlıyoruz.server.listen()sunucuyu belirtilen portta başlatır.
Uygulamanın Test Edilmesi
Her şey hazır! Şimdi sunucuyu çalıştırıp GraphQL API’mizi test edelim.
npm run dev
Tarayıcınızı http://localhost:4000 adresine yönlendirin (eğer port 4000 ise). Apollo Sandbox veya GraphQL Playground arayüzü açılacaktır.
Örnek Sorgular (Queries)
Yeni Kullanıcı Oluşturma
mutation CreateUser {
createUser(input: { username: "john_doe", email: "john@example.com" }) {
id
username
email
}
}
Tüm Kullanıcıları Getirme
query GetUsers {
users {
id
username
email
posts {
id
title
}
}
}
Belirli Bir Kullanıcıyı ID ile Getirme
query GetUserById {
user(id: "1") {
id
username
email
posts {
id
title
content
}
}
}
Yeni Gönderi Oluşturma
(Önce yukarıdaki CreateUser ile bir kullanıcı oluşturduğunuzdan emin olun ve userId‘ı güncelleyin.)
mutation CreatePost {
createPost(input: { title: "My First Post", content: "Hello GraphQL with Sequelize!", userId: "1" }) {
id
title
content
author {
username
}
}
}
Tüm Gönderileri Getirme
query GetPosts {
posts {
id
title
content
author {
id
username
email
}
}
}
Belirli Bir Gönderiyi ID ile Getirme
query GetPostById {
post(id: "1") {
id
title
content
author {
username
email
}
}
}
Örnek Mutasyonlar (Mutations)
Kullanıcı Güncelleme
mutation UpdateUser {
updateUser(id: "1", input: { username: "john_doe_updated" }) {
id
username
email
}
}
Gönderi Silme
mutation DeletePost {
deletePost(id: "1")
}
Bu sorgu ve mutasyonları deneyerek API’nizin beklediğiniz gibi çalıştığını doğrulayabilirsiniz.
Gelişmiş Konular ve En İyi Uygulamalar
Kimlik Doğrulama ve Yetkilendirme (Authentication & Authorization)
Gerçek dünya uygulamalarında, kullanıcıların API’ye erişimini ve belirli işlemleri yapma yetkilerini kontrol etmeniz gerekir. Apollo Server, context objesini kullanarak kimlik doğrulama ve yetkilendirme bilgilerini çözümleyicilere iletmek için mükemmel bir yol sunar.
context fonksiyonunda bir kullanıcının kimlik bilgilerini (örneğin JWT token’dan) ayrıştırabilir ve db objesiyle birlikte çözümleyicilere iletebilirsiniz:
// src/index.js (context örneği)
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => {
// HTTP başlıklarından token'ı al
const token = req.headers.authorization || '';
// Token'ı doğrula ve kullanıcı bilgilerini çıkar
// Örneğin, JWT.verify(token) ile
const user = { id: 1, username: 'mockUser' }; // Gerçek bir kullanıcı objesiyle değiştirin
return { db, user }; // Kullanıcı objesini context'e ekle
},
});
Çözümleyicilerinizde bu user objesini kullanarak yetkilendirme kontrolleri yapabilirsiniz:
// src/graphql/resolvers.js (yetkilendirme örneği)
Mutation: {
createPost: async (parent, { input }, { db, user }) => {
if (!user) throw new Error('Authentication required');
// ... post oluşturma mantığı
}
}
Hata Yönetimi (Error Handling)
GraphQL, hata yönetimi için standart bir yapıya sahiptir. Apollo Server, hataları otomatik olarak işler. Ancak, hataları daha okunabilir hale getirmek veya hassas bilgileri gizlemek için formatError seçeneğini kullanabilirsiniz:
// src/index.js (formatError örneği)
const server = new ApolloServer({
// ...
formatError: (error) => {
if (error.extensions.code === 'UNAUTHENTICATED') {
return new Error('Authentication error: Could not authenticate with given token.');
}
// Diğer hata türleri için özel işlem yapabilirsiniz
return error;
},
});
Veri Yükleyici (DataLoader) Kullanımı
Yukarıdaki User ve Post çözümleyicilerindeki ilişkisel veri yükleme yönteminde (örneğin, bir kullanıcı için tüm gönderileri getirme), her kullanıcı için ayrı bir veritabanı sorgusu yapıldığını fark etmiş olabilirsiniz. Bu, N+1 sorgu problemine yol açar ve performansı olumsuz etkileyebilir. DataLoader kütüphanesi bu sorunu çözmek için tasarlanmıştır. DataLoader, aynı anda birden fazla isteği toplar (batching) ve önbelleğe alır (caching), böylece veritabanına yapılan sorgu sayısını önemli ölçüde azaltır.
src/index.js dosyasında context‘e DataLoader’ı ekleyebiliriz:
// src/index.js (DataLoader entegrasyonu)
const DataLoader = require('dataloader');
// ...
async function startApolloServer() {
await db.sequelize.sync({ alter: true });
const server = new ApolloServer({
typeDefs,
resolvers,
context: () => ({
db,
// DataLoader örnekleri
userLoader: new DataLoader(async (ids) => {
const users = await db.User.findAll({ where: { id: ids } });
return ids.map(id => users.find(user => user.id === id));
}),
postLoader: new DataLoader(async (ids) => {
const posts = await db.Post.findAll({ where: { id: ids } });
return ids.map(id => posts.find(post => post.id === id));
})
// Diğer DataLoader'lar...
}),
formatError: (error) => {
console.error(error);
return error;
},
});
const { url } = await server.listen({ port: process.env.PORT || 4000 });
console.log( Server ready at ${url});
}
Ve src/graphql/resolvers.js dosyasında DataLoader’ı kullanabiliriz:
// src/graphql/resolvers.js (DataLoader kullanımı)
const resolvers = {
// ...
User: {
posts: async (parent, args, { db, postLoader }) => {
// Bir kullanıcının tüm gönderilerini getirmek için DataLoader kullanmak daha karmaşıktır
// Çünkü DataLoader genellikle ID'ye göre tekil öğeleri yükler.
// İlişkisel veriler için ayrı bir DataLoader yazmak gerekebilir
// veya Sequelize'ın eager loading özelliğini Query seviyesinde kullanmak daha mantıklı olabilir.
// Ancak, eğer bir Post'un yazarını yüklerken N+1 problemi yaşanıyorsa:
// return postLoader.loadMany(parent.postIds); // Eğer parent'ta post ID'leri varsa
return db.Post.findAll({ where: { userId: parent.id } }); // Şimdilik bu şekilde kalabilir
}
},
Post: {
author: async (parent, args, { userLoader }) => {
return userLoader.load(parent.userId); // N+1 problemini çözer
}
}
};
Post tipindeki author çözümleyicisinde userLoader.load(parent.userId) kullanımı, birden fazla gönderinin aynı yazara sahip olması durumunda yalnızca tek bir veritabanı sorgusu yapılmasını sağlayacaktır.
TypeScript Kullanımı
Büyük ölçekli projelerde TypeScript kullanmak, kod kalitesini artırır ve hataları derleme zamanında yakalamanızı sağlar. Apollo Server ve Sequelize, TypeScript ile mükemmel uyumludur. Bu makale JavaScript tabanlı olsa da, projeyi TypeScript’e taşımak için @types/apollo-server, @types/graphql, @types/sequelize gibi tip tanımlamalarını kurmanız ve kodunuzu .ts uzantılı dosyalar halinde yazmanız yeterlidir.
Sonuç
Bu makalede, Node.js ortamında GraphQL sunucusu kurmak için Apollo Server’ı ve ilişkisel veritabanı yönetimini kolaylaştıran Sequelize’ı nasıl entegre edeceğinizi adım adım öğrendiniz. Proje yapılandırmasından başlayarak, Sequelize modellerini tanımladık, GraphQL şemamızı ve çözümleyicilerimizi oluşturduk ve son olarak Apollo Server’ı başlatarak API’mizi test ettik. Ayrıca, kimlik doğrulama, hata yönetimi ve N+1 sorgu problemini çözmek için DataLoader gibi gelişmiş konulara da değindik.
GraphQL’in esnekliği, Apollo Server’ın geliştirici dostu yapısı ve Sequelize’ın güçlü ORM yetenekleri birleştiğinde, modern ve ölçeklenebilir web uygulamaları için son derece etkili bir API katmanı oluşturabilirsiniz. Bu kurulum, projenizin temelini oluşturacak ve gelecekteki geliştirmeleriniz için sağlam bir başlangıç noktası sağlayacaktır. Veritabanı etkileşimlerinizi GraphQL ile nasıl yöneteceğinizi anlayarak, istemci tarafı uygulamalarınız için daha verimli ve optimize edilmiş veri erişimi sağlayabilirsiniz.