GraphQL ve Vue ile Dosya İşleme Uygulaması Nasıl Geliştirilir?
Giriş
Günümüz web uygulamalarının ayrılmaz bir parçası olan dosya yönetimi, kullanıcıların belge, görsel, video gibi çeşitli medya türlerini yüklemesine, indirmesine, görüntülemesine ve silmesine olanak tanır. Bu tür işlevsellikleri sağlamak için güçlü ve esnek bir mimariye ihtiyaç duyarız. Geleneksel REST API’leri bu konuda iyi bir iş çıkarsa da, GraphQL’in sunduğu esneklik ve veri çekme gücü, özellikle karmaşık veri yapıları ve kaynaklar arası ilişkilerin olduğu senaryolarda önemli avantajlar sağlayabilir. Bu makalede, modern bir dosya işleme uygulamasını GraphQL’i bir arka uç teknolojisi olarak kullanarak ve Vue.js’i ön yüz çerçevesi olarak tercih ederek nasıl geliştireceğimizi ayrıntılı bir şekilde inceleyeceğiz. Adım adım bir geliştirme süreciyle, hem arka uçta hem de ön yüzde dosya yükleme, listeleme ve silme gibi temel işlevleri nasıl uygulayacağımızı öğreneceğiz.
Dosya İşlemede Neden GraphQL Tercih Edilmeli?
GraphQL, API’ler için bir sorgu dili ve çalışma zamanı olup, istemcilerin tam olarak ihtiyaç duydukları veriyi istemesine olanak tanır. Bu, özellikle dosya işleme gibi, dosyanın kendisiyle birlikte metadata (dosya adı, boyutu, türü, yüklenme tarihi vb.) yönetilmesi gereken durumlarda büyük faydalar sağlar.
REST ve GraphQL Karşılaştırması
Geleneksel REST API’lerinde, dosya yükleme genellikle ayrı bir endpoint (örn. /upload) üzerinden bir multipart/form-data isteği ile yapılır. Dosya metadata’sı ayrı bir istek veya aynı isteğin bir parçası olarak gönderilebilir. Dosyaları listelemek için başka bir endpoint (örn. /files) kullanılır ve genellikle tüm metadata seti geri döner.
GraphQL ise, dosya yüklemeyi “GraphQL Multipart Request Specification” adı verilen özel bir yöntemle ele alır. Bu yöntem, bir GraphQL mutasyonunun bir parçası olarak dosya verilerini göndermeyi mümkün kılar. Bu sayede, dosya yükleme işlemi sırasında dosya metadata’sını ve hatta diğer ilgili verileri (örneğin, dosyanın hangi kullanıcıya ait olduğu, hangi projeye yüklendiği) tek bir istekte gönderebiliriz. Benzer şekilde, dosyaları listelerken, istemci yalnızca ihtiyaç duyduğu metadata alanlarını sorgulayabilir, bu da ağ trafiğini azaltır ve performansı artırır.
GraphQL’in Avantajları
* Tek İstekle Çoklu İşlem: Dosya yüklerken metadata ile birlikte diğer ilgili verileri tek bir mutasyon içinde gönderebilme.
* Veri Aşırı Getirme/Az Getirme Sorunlarının Önlenmesi: İstemci sadece ihtiyaç duyduğu dosya metadata alanlarını sorgular. Bu, özellikle çok sayıda dosya listelenirken bant genişliğinden tasarruf sağlar.
* Güçlü Tip Sistemi: GraphQL’in tip sistemi, dosya yükleme ve metadata yönetimi için net ve güvenli bir API tanımı sunar. Bu, geliştirme sürecini kolaylaştırır ve hataları azaltır.
* Geliştirici Deneyimi: GraphQL araçları (örneğin, GraphiQL, Apollo Studio) API’yi keşfetmeyi ve test etmeyi kolaylaştırır.
Temel Teknolojiler
Bu uygulamayı geliştirmek için aşağıdaki temel teknolojileri kullanacağız:
GraphQL
Arka uçta API katmanını oluşturmak için GraphQL kullanacağız. Genellikle Node.js ekosisteminde Apollo Server veya NestJS gibi çerçeveler tercih edilir. Bu makalede, Apollo Server’ı Node.js ve Express ile birlikte kullanacağız.
Vue.js
Ön yüz kullanıcı arayüzünü oluşturmak için progresif JavaScript çerçevesi Vue.js’i kullanacağız. Vue’nun reaktif veri bağlama ve bileşen tabanlı yapısı, dosya listeleme ve yükleme arayüzlerini geliştirmeyi kolaylaştırır.
Arka Uç Çerçevesi ve Kütüphaneler
* Node.js & Express: Arka uç sunucusunu kurmak için.
* Apollo Server: GraphQL sunucusunu uygulamak için.
* graphql-upload: GraphQL ile dosya yüklemeyi desteklemek için standart bir çözüm.
* multer (isteğe bağlı, alternatif olarak fs modülü): Dosyaları sunucu tarafında depolamak için.
* Veritabanı (MongoDB/PostgreSQL): Dosyaların metadata’sını (adı, boyutu, türü, depolama yolu vb.) saklamak için. Bu makalede basitlik adına yerel dosya sistemini ve bir veritabanı olmadan ilerleyebiliriz, ancak gerçek dünya uygulamalarında veritabanı kullanımı kritik öneme sahiptir.
Ön Yüz Kütüphaneleri
* Vue CLI: Vue projesini hızlıca başlatmak için.
* Apollo Client: Vue uygulamasından GraphQL API’sine istekler göndermek için.
* apollo-upload-client: Apollo Client ile dosya yüklemeyi desteklemek için özel bir bağlantı (link).
Arka Uç Uygulaması (Node.js, Express, Apollo Server)
Proje Kurulumu
İlk olarak, yeni bir Node.js projesi oluşturalım:
mkdir graphql-file-upload-backend
cd graphql-file-upload-backend
npm init -y
npm install express apollo-server graphql graphql-upload
npm install --save-dev nodemon
package.json dosyanıza start ve dev script’lerini ekleyin:
"scripts": {
"start": "node src/index.js",
"dev": "nodemon src/index.js"
}
GraphQL Şema Tanımı
Dosya yükleme, listeleme ve silme için GraphQL şemasını tanımlayalım. Upload scalar tipi graphql-upload kütüphanesi tarafından sağlanır ve dosya verilerini temsil eder.
src/schema.js:
const { gql } = require('apollo-server-express');
const typeDefs = gql
scalar Upload
type File {
id: ID!
filename: String!
mimetype: String!
encoding: String!
url: String!
size: Float
}
type Query {
hello: String
files: [File!]!
}
type Mutation {
singleUpload(file: Upload!): File!
deleteFile(id: ID!): Boolean!
}
;
module.exports = typeDefs;
Burada:
* Upload: Dosya yüklemeleri için özel bir scalar tip.
* File: Yüklenen bir dosyanın metadata’sını temsil eden bir tip. id, filename, mimetype, encoding, url (dosyaya erişim yolu) ve size (boyut) içerir.
* Query: files sorgusu ile tüm yüklenen dosyaları listeleriz.
* Mutation:
* singleUpload: Tek bir dosya yüklemek için kullanılır. Upload tipinde bir file argümanı alır ve yüklenen dosyanın File tipinde metadata’sını döner.
* deleteFile: Belirli bir dosyayı silmek için kullanılır. id argümanı alır ve işlemin başarılı olup olmadığını belirten bir Boolean döner.
Resolvers Uygulaması
Resolvers, şemadaki sorgu ve mutasyonları gerçek veri ve mantıkla eşleştirir. Dosya yükleme ve depolama için graphql-upload‘dan gelen processUpload fonksiyonunu kullanacağız. Dosyaları yerel olarak uploads dizinine kaydedeceğiz.
src/resolvers.js:
const { GraphQLUpload } = require('graphql-upload');
const path = require('path');
const { createWriteStream, unlink } = require('fs');
const shortid = require('shortid');
// Basit bir bellek içi dosya deposu (gerçek uygulamada veritabanı kullanılmalı)
const files = [];
const storeUpload = async ({ stream, filename, mimetype, encoding }) => {
const id = shortid.generate();
const fileExtension = path.extname(filename);
const newFilename = ${id}${fileExtension};
const uploadDir = path.join(__dirname, '../uploads');
const filePath = path.join(uploadDir, newFilename);
const fileUrl = /uploads/${newFilename}; // Frontend'den erişim için URL
// Dosyayı diske yaz
await new Promise((resolve, reject) =>
stream
.pipe(createWriteStream(filePath))
.on('finish', resolve)
.on('error', reject)
);
return { id, filename: newFilename, mimetype, encoding, url: fileUrl, filePath };
};
const resolvers = {
Upload: GraphQLUpload,
Query: {
hello: () => 'Merhaba GraphQL Dosya Yükleme!',
files: () => files.map(file => ({ ...file, url: file.url })),
},
Mutation: {
singleUpload: async (parent, { file }) => {
const { createReadStream, filename, mimetype, encoding } = await file;
const stream = createReadStream();
const storedFile = await storeUpload({ stream, filename, mimetype, encoding });
// Dosya boyutunu eklemek için stream'i yeniden okuyabiliriz veya başka bir yöntem kullanabiliriz.
// Basitlik adına burada boyut bilgisini atlıyoruz veya varsayılan bir değer veriyoruz.
// Gerçek uygulamada dosya boyutunu dosya sisteminden okuyabilirsiniz.
const stats = await new Promise((resolve, reject) => {
fs.stat(storedFile.filePath, (err, stats) => {
if (err) reject(err);
resolve(stats);
});
});
storedFile.size = stats.size;
files.push(storedFile); // Bellek içi depoya ekle
return storedFile;
},
deleteFile: async (parent, { id }) => {
const fileIndex = files.findIndex(f => f.id === id);
if (fileIndex === -1) {
return false; // Dosya bulunamadı
}
const fileToDelete = files[fileIndex];
// Yerel dosyayı sil
await new Promise((resolve, reject) => {
unlink(fileToDelete.filePath, (err) => {
if (err) reject(err);
resolve();
});
});
files.splice(fileIndex, 1); // Bellek içi depodan kaldır
return true;
},
},
};
module.exports = resolvers;
Not: Yukarıdaki files dizisi, dosyaların metadata’sını bellekte tutan basit bir yapıdır. Gerçek bir uygulamada bu verileri bir veritabanında (MongoDB, PostgreSQL vb.) saklamanız gerekir. filePath alanı, dosya silme işlemi için gereklidir.
Sunucu Kurulumu
Express uygulamasını ve Apollo Server’ı yapılandıralım. graphql-upload‘ın middleware’ini Express’e eklemeyi unutmayın. Ayrıca, yüklenen dosyaların sunulabilmesi için bir statik dosya sunucu middleware’i eklemeliyiz.
src/index.js:
const express = require('express');
const { ApolloServer } = require('apollo-server-express');
const { graphqlUploadExpress } = require('graphql-upload');
const typeDefs = require('./schema');
const resolvers = require('./resolvers');
const path = require('path');
const fs = require('fs');
async function startApolloServer() {
const app = express();
// Yüklenen dosyaların depolanacağı dizini oluştur
const uploadDir = path.join(__dirname, '../uploads');
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir);
}
// GraphQL dosya yükleme middleware'ini etkinleştir
app.use(graphqlUploadExpress({ maxFileSize: 10000000, maxFiles: 10 })); // Maksimum 10MB dosya boyutu
const server = new ApolloServer({
typeDefs,
resolvers,
uploads: false, // graphqlUploadExpress zaten dosya işleme yapıyor
});
await server.start();
server.applyMiddleware({ app });
// Yüklenen dosyaları statik olarak sun
app.use('/uploads', express.static(uploadDir));
const PORT = process.env.PORT || 4000;
app.listen(PORT, () => {
console.log( Sunucu http://localhost:${PORT}${server.graphqlPath} adresinde çalışıyor);
console.log( Dosyalar http://localhost:${PORT}/uploads/ adresinden erişilebilir);
});
}
startApolloServer();
Artık npm run dev komutu ile arka ucu çalıştırabilirsiniz. Apollo Studio veya GraphiQL arayüzünden sorgularınızı test edebilirsiniz.
Ön Yüz Uygulaması (Vue.js)
Vue Projesi Kurulumu
Vue CLI kullanarak yeni bir Vue projesi oluşturalım:
vue create graphql-file-upload-frontend
cd graphql-file-upload-frontend
npm install vue-apollo graphql apollo-client apollo-link-http apollo-cache-inmemory apollo-upload-client graphql-tag
GraphQL İstemcisi Kurulumu
main.js veya ayrı bir apollo.js dosyasında Apollo Client’ı yapılandırmamız gerekiyor. Özellikle apollo-upload-client‘ı kullanarak dosya yüklemeyi desteklemeliyiz.
src/apollo.js:
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { ApolloClient } from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { createUploadLink } from 'apollo-upload-client';
Vue.use(VueApollo);
// HTTP bağlantısı oluştur (dosya yükleme için özel link kullanacağız)
// const httpLink = createHttpLink({
// uri: 'http://localhost:4000/graphql',
// });
// Dosya yükleme için özel link oluştur
const uploadLink = createUploadLink({
uri: 'http://localhost:4000/graphql',
});
// Cache oluştur
const cache = new InMemoryCache();
// Apollo Client örneği oluştur
const apolloClient = new ApolloClient({
link: uploadLink, // Dosya yükleme linkini kullan
cache,
});
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
});
export default apolloProvider;
src/main.js:
import Vue from 'vue';
import App from './App.vue';
import apolloProvider from './apollo'; // Apollo sağlayıcısını import et
Vue.config.productionTip = false;
new Vue({
apolloProvider, // Apollo sağlayıcısını Vue örneğine ekle
render: h => h(App),
}).$mount('#app');
Dosya Yükleme Bileşeni
Bir dosya yükleme bileşeni oluşturalım. Bu bileşen, bir dosya seçici () ve bir yükleme düğmesi içerecektir.
src/components/FileUpload.vue:
Dosya Yükle
Yükleniyor...
Hata: {{ uploadError }}
Yüklendi: {{ uploadedFile.filename }}
Dosya Listeleme Bileşeni
Yüklenen dosyaları sunucudan çekip listeleyen bir bileşen oluşturalım.
src/components/FileList.vue:
Yüklenen Dosyalar
Dosyalar yükleniyor...
Henüz dosya yüklenmedi.
-
{{ file.filename }}
({{ (file.size / 1024 / 1024).toFixed(2) }} MB)
Silme Hatası: {{ deleteError }}
Ana Uygulama Bileşeni
App.vue bileşeninde bu iki bileşeni bir araya getirelim.
src/App.vue:
Artık npm run serve komutu ile ön yüz uygulamasını çalıştırabilirsiniz. Uygulamanızda dosya yükleme ve listeleme işlevlerinin çalıştığını görmelisiniz.
Gelişmiş Konular ve En İyi Uygulamalar
Güvenlik
* Kimlik Doğrulama ve Yetkilendirme: Gerçek bir uygulamada, dosyaları kimlerin yükleyebileceğini, görüntüleyebileceğini veya silebileceğini kontrol etmek için kullanıcı kimlik doğrulama (JWT, OAuth vb.) ve yetkilendirme (rol tabanlı erişim kontrolü) mekanizmaları eklemelisiniz. GraphQL resolvers’ınızda bu kontrolleri uygulamalısınız.
* Dosya Türü ve Boyut Doğrulaması: Kötü niyetli dosyaların yüklenmesini önlemek için sunucu tarafında dosya türünü (MIME tipi) ve boyutunu mutlaka doğrulayın. graphql-upload ve Express middleware’leri (örn. graphqlUploadExpress) bu konuda yardımcı olabilir.
* Dosya Adı Sanitizasyonu: Yüklenen dosya adlarını güvenlik açıklarını (path traversal gibi) önlemek için temizleyin.
* Virüs Taraması: Hassas uygulamalarda, yüklenen dosyaların virüs taramasından geçirilmesi düşünülebilir.
Hata Yönetimi
Hem ön yüzde hem de arka uçta kapsamlı hata yönetimi uygulayın. GraphQL’in hata yanıtlarını uygun şekilde işleyin ve kullanıcıya anlamlı geri bildirimler sağlayın. Sunucu tarafında loglama mekanizmaları kurun.
Ölçeklenebilirlik ve Depolama
* Bulut Depolama: Üretim ortamlarında yerel dosya sistemi yerine Amazon S3, Google Cloud Storage veya Azure Blob Storage gibi bulut depolama hizmetleri kullanın. Bu hizmetler, yüksek erişilebilirlik, dayanıklılık ve ölçeklenebilirlik sunar.
* CDN (İçerik Dağıtım Ağı): Dosyaları daha hızlı sunmak için bir CDN kullanmayı düşünün. CDN’ler, dosyalarınızı coğrafi olarak kullanıcılara yakın sunucularda önbelleğe alarak yükleme sürelerini azaltır.
Performans
* Tembel Yükleme (Lazy Loading): Özellikle çok sayıda dosya veya büyük görseller listelenirken, tüm dosyaları aynı anda yüklemek yerine, kullanıcı kaydırdıkça dosyaları yüklemek için tembel yükleme veya sonsuz kaydırma tekniklerini kullanın.
* Küçültme ve Sıkıştırma: Görsel gibi medya dosyalarını yüklemeden önce veya sonra optimize edin (boyutlarını küçültün, sıkıştırın).
* Önbellekleme: GraphQL sorgularını ve dosya metadata’sını önbelleğe alarak performansı artırabilirsiniz.
Test
Uygulamanızın hem arka ucunu (GraphQL resolvers, dosya depolama mantığı) hem de ön yüzünü (bileşenler, GraphQL entegrasyonu) birim ve entegrasyon testleri ile kapsamlı bir şekilde test edin.
Sonuç
Bu makalede, GraphQL ve Vue.js kullanarak temel bir dosya işleme uygulamasının nasıl geliştirileceğini detaylı bir şekilde inceledik. Arka uçta Apollo Server ve graphql-upload ile dosya yükleme, listeleme ve silme mutasyonlarını tanımladık ve uyguladık. Ön yüzde ise Vue.js ve Apollo Client ile dosya seçme, yükleme ve sunucudan çekilen dosyaları görüntüleme işlevlerini geliştirdik.
GraphQL’in tip güvenliği ve esnek sorgulama yetenekleri, dosya metadata’sını yönetme ve istemcinin ihtiyaç duyduğu veriyi tam olarak çekme konusunda önemli avantajlar sunar. Vue.js’in reaktif ve bileşen tabanlı yapısı ise kullanıcı arayüzünü oluşturmayı ve yönetmeyi kolaylaştırır.
Gerçek dünya uygulamaları için güvenlik, ölçeklenebilirlik, hata yönetimi ve performans gibi gelişmiş konuları göz önünde bulundurarak bu temel yapıyı daha da geliştirmek mümkündür. Bulut depolama, CDN entegrasyonu ve kapsamlı yetkilendirme mekanizmaları, sağlam ve üretim düzeyinde bir dosya yönetim sistemi oluşturmak için atılması gereken sonraki adımlardır.