{"id":41232,"date":"2026-03-13T14:02:23","date_gmt":"2026-03-13T11:02:23","guid":{"rendered":"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/"},"modified":"2026-03-13T14:02:23","modified_gmt":"2026-03-13T11:02:23","slug":"graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli","status":"publish","type":"post","link":"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/","title":{"rendered":"GraphQL Nedir ve Dosya Y\u00fcklemede Neden Tercih Edilmeli?"},"content":{"rendered":"<p><title>Go ile GraphQL API: Dosya Y\u00fckleme \u0130\u015flemlerini Spaces&#8217;a Nas\u0131l Ta\u015f\u0131rs\u0131n\u0131z?<\/title><br \/>\n<body><\/p>\n<p>Modern web uygulamalar\u0131, kullan\u0131c\u0131lar\u0131n dosya y\u00fcklemesine s\u0131k\u00e7a ihtiya\u00e7 duyar. Foto\u011fraflar, videolar, belgeler veya di\u011fer medya t\u00fcrleri olsun, bu dosyalar\u0131n g\u00fcvenli, \u00f6l\u00e7eklenebilir ve eri\u015filebilir bir \u015fekilde depolanmas\u0131 kritik \u00f6neme sahiptir. Geleneksel dosya y\u00fckleme y\u00f6ntemleri karma\u015f\u0131k olabilirken, Go dilinin performans\u0131 ve GraphQL&#8217;in esnekli\u011fi bir araya geldi\u011finde, bu s\u00fcre\u00e7 olduk\u00e7a basitle\u015febilir. Bu makale, Go ve GraphQL kullanarak dosyalar\u0131 S3 uyumlu Object Storage (Spaces gibi) hizmetlerine y\u00fcklemek i\u00e7in g\u00fc\u00e7l\u00fc bir API nas\u0131l olu\u015fturulaca\u011f\u0131n\u0131 ad\u0131m ad\u0131m a\u00e7\u0131klayacakt\u0131r.<\/p>\n<h2>GraphQL Nedir ve Dosya Y\u00fcklemede Neden Tercih Edilmeli?<\/h2>\n<p>GraphQL, API&#8217;ler i\u00e7in bir sorgu dili ve \u00e7al\u0131\u015fma zaman\u0131d\u0131r. Facebook taraf\u0131ndan geli\u015ftirilen ve a\u00e7\u0131k kaynak hale getirilen GraphQL, istemcilerin tam olarak ihtiya\u00e7 duyduklar\u0131 veriyi tek bir istekte almalar\u0131n\u0131 sa\u011flar. RESTful API&#8217;lerin aksine, GraphQL&#8217;de istemci, hangi veriye ihtiyac\u0131 oldu\u011funu belirten bir sorgu g\u00f6nderir ve sunucu yaln\u0131zca o veriyi d\u00f6nd\u00fcr\u00fcr. Bu, &#8220;a\u015f\u0131r\u0131 veri \u00e7ekme&#8221; (over-fetching) veya &#8220;eksik veri \u00e7ekme&#8221; (under-fetching) sorunlar\u0131n\u0131 ortadan kald\u0131r\u0131r ve a\u011f trafi\u011fini optimize eder. Peki, dosya y\u00fckleme gibi ikili (binary) veri i\u015flemlerinde GraphQL neden tercih edilmeli?<\/p>\n<p>Geleneksel REST API&#8217;lerde dosya y\u00fckleme genellikle ayr\u0131 bir endpoint (\u00f6rn. <code>POST \/upload<\/code>) \u00fczerinden <code>multipart\/form-data<\/code> format\u0131nda ger\u00e7ekle\u015ftirilir. GraphQL de benzer bir mekanizmay\u0131 destekler ancak bunu kendi \u015fema tan\u0131m\u0131 i\u00e7ine entegre eder. GraphQL&#8217;in en b\u00fcy\u00fck avantajlar\u0131ndan biri, API&#8217;nizin t\u00fcm yeteneklerini tek bir \u015fema alt\u0131nda tan\u0131mlayabilmenizdir. Dosya y\u00fckleme, di\u011fer t\u00fcm veri i\u015flemleri (sorgular, mutasyonlar) gibi bir mutasyon olarak tan\u0131mlan\u0131r ve bu, API&#8217;nizin tutarl\u0131l\u0131\u011f\u0131n\u0131 art\u0131r\u0131r. Ayr\u0131ca, GraphQL istemcileri, dosya y\u00fckleme mutasyonuna ek olarak, y\u00fcklenen dosyan\u0131n metadata&#8217;s\u0131n\u0131 (URL, boyut, t\u00fcr gibi) hemen geri alabilirler. Bu, istemci taraf\u0131nda ek bir istek yapma ihtiyac\u0131n\u0131 ortadan kald\u0131r\u0131r ve geli\u015ftirme s\u00fcrecini h\u0131zland\u0131r\u0131r. \u00d6rne\u011fin, bir kullan\u0131c\u0131 profil foto\u011fraf\u0131n\u0131 y\u00fckledi\u011finde, sunucu foto\u011fraf\u0131 depolay\u0131p, hemen ard\u0131ndan g\u00fcncellenmi\u015f profil bilgilerini ve foto\u011fraf\u0131n yeni URL&#8217;sini tek bir GraphQL yan\u0131t\u0131nda d\u00f6nd\u00fcrebilir. Bu entegrasyon, kullan\u0131c\u0131 deneyimini iyile\u015ftirirken, geli\u015ftiricilerin de daha az kod yazmas\u0131n\u0131 sa\u011flar. \u00d6zellikle mikroservis mimarilerinde veya karma\u015f\u0131k veri modellerine sahip uygulamalarda, GraphQL&#8217;in sundu\u011fu bu esneklik, dosya y\u00fckleme i\u015flemlerini y\u00f6netmeyi \u00e7ok daha basit ve \u00f6ng\u00f6r\u00fclebilir hale getirir. Bu yakla\u015f\u0131m, API&#8217;nizin zamanla evrim ge\u00e7irmesini de kolayla\u015ft\u0131r\u0131r, zira yeni dosya t\u00fcrleri veya y\u00fckleme gereksinimleri ortaya \u00e7\u0131kt\u0131\u011f\u0131nda, \u015fema \u00fczerinde yap\u0131lacak k\u00fc\u00e7\u00fck de\u011fi\u015fikliklerle mevcut yap\u0131ya kolayca adapte olunabilir.<\/p>\n<h3>Go Neden Bu Mimari \u0130\u00e7in \u0130deal Bir Se\u00e7imdir?<\/h3>\n<p>Go (Golang), Google taraf\u0131ndan geli\u015ftirilen, derlenmi\u015f, statik olarak yaz\u0131lm\u0131\u015f bir programlama dilidir. Performans\u0131, e\u015fzamanl\u0131l\u0131k yetenekleri ve basit s\u00f6zdizimi sayesinde modern, y\u00fcksek performansl\u0131 backend servisleri geli\u015ftirmek i\u00e7in pop\u00fcler bir tercihtir. Dosya y\u00fckleme gibi I\/O yo\u011fun i\u015flemler i\u00e7in Go&#8217;nun sundu\u011fu avantajlar saymakla bitmez:<\/p>\n<ul>\n<li><strong>E\u015fzamanl\u0131l\u0131k (Concurrency):<\/strong> Go&#8217;nun goroutine&#8217;leri ve kanallar\u0131, birden fazla dosya y\u00fckleme iste\u011fini ayn\u0131 anda ve verimli bir \u015fekilde i\u015flemenize olanak tan\u0131r. Her bir y\u00fckleme iste\u011fi i\u00e7in hafif bir goroutine ba\u015flat\u0131labilir, bu da sunucunun di\u011fer i\u015flemleri engellemeden \u00e7al\u0131\u015fmaya devam etmesini sa\u011flar. Bu, \u00f6zellikle y\u00fcksek trafikli uygulamalarda yan\u0131t s\u00fcrelerini \u00f6nemli \u00f6l\u00e7\u00fcde iyile\u015ftirir.<\/li>\n<li><strong>Performans:<\/strong> Go, derlenmi\u015f bir dil oldu\u011fu i\u00e7in \u00e7al\u0131\u015fma zaman\u0131 performans\u0131 olduk\u00e7a y\u00fcksektir. D\u00fc\u015f\u00fck bellek kullan\u0131m\u0131 ve h\u0131zl\u0131 i\u015flem yetene\u011fi, b\u00fcy\u00fck dosyalar\u0131n i\u015flenmesi ve a\u011f \u00fczerinden aktar\u0131lmas\u0131 s\u0131ras\u0131nda kritik bir avantaj sa\u011flar. Bu, sunucu kaynaklar\u0131n\u0131n daha verimli kullan\u0131lmas\u0131na ve maliyetlerin d\u00fc\u015f\u00fcr\u00fclmesine yard\u0131mc\u0131 olur.<\/li>\n<li><strong>Basitlik ve Okunabilirlik:<\/strong> Go&#8217;nun temiz ve minimalist s\u00f6zdizimi, kodun yaz\u0131lmas\u0131n\u0131 ve bak\u0131m\u0131n\u0131 kolayla\u015ft\u0131r\u0131r. Bu, \u00f6zellikle b\u00fcy\u00fck ekiplerde veya uzun vadeli projelerde geli\u015ftirme h\u0131z\u0131n\u0131 art\u0131r\u0131r ve hatalar\u0131 azalt\u0131r.<\/li>\n<li><strong>Kapsaml\u0131 Standart K\u00fct\u00fcphane:<\/strong> Go, a\u011f i\u015flemleri, dosya sistemi etkile\u015fimleri ve kriptografi gibi bir\u00e7ok yayg\u0131n g\u00f6revi destekleyen zengin bir standart k\u00fct\u00fcphaneye sahiptir. Bu, harici ba\u011f\u0131ml\u0131l\u0131klara olan ihtiyac\u0131 azalt\u0131r ve geli\u015ftirme s\u00fcrecini basitle\u015ftirir.<\/li>\n<li><strong>G\u00fc\u00e7l\u00fc Ekosistem:<\/strong> GraphQL i\u00e7in <code>99designs\/gqlgen<\/code> ve S3 uyumlu depolama hizmetleriyle entegrasyon i\u00e7in AWS SDK for Go gibi olgun ve iyi desteklenen k\u00fct\u00fcphaneler mevcuttur. Bu k\u00fct\u00fcphaneler, geli\u015ftiricilerin karma\u015f\u0131k i\u015flevleri h\u0131zl\u0131 bir \u015fekilde uygulamas\u0131na olanak tan\u0131r.<\/li>\n<\/ul>\n<p>Bu \u00f6zellikler, Go&#8217;yu dosya y\u00fckleme ve genel API geli\u015ftirme i\u00e7in sa\u011flam ve g\u00fcvenilir bir se\u00e7im haline getirir. Go&#8217;nun derlenmi\u015f yap\u0131s\u0131 sayesinde, uygulaman\u0131z tek bir ikili dosya olarak da\u011f\u0131t\u0131labilir, bu da da\u011f\u0131t\u0131m s\u00fcre\u00e7lerini basitle\u015ftirir. Ayr\u0131ca, statik tipleme, derleme zaman\u0131nda bir\u00e7ok hatan\u0131n yakalanmas\u0131na yard\u0131mc\u0131 olur, bu da \u00e7al\u0131\u015fma zaman\u0131 hatalar\u0131n\u0131 azalt\u0131r ve uygulaman\u0131n kararl\u0131l\u0131\u011f\u0131n\u0131 art\u0131r\u0131r. Sonu\u00e7 olarak, Go ile geli\u015ftirilen bir GraphQL API, hem performans hem de s\u00fcrd\u00fcr\u00fclebilirlik a\u00e7\u0131s\u0131ndan beklentileri kar\u015f\u0131layacakt\u0131r.<\/p>\n<h2>Object Storage (Spaces) Nedir ve Nas\u0131l \u00c7al\u0131\u015f\u0131r?<\/h2>\n<p>Object Storage (Nesne Depolama), verileri nesneler olarak depolayan, \u00f6l\u00e7eklenebilir ve dayan\u0131kl\u0131 bir depolama mimarisidir. Geleneksel dosya sistemlerinin (\u00f6rne\u011fin, sunucudaki bir dizin) veya blok depolaman\u0131n (veritaban\u0131 diskleri) aksine, nesne depolama, verileri hiyerar\u015fik bir yap\u0131 yerine d\u00fcz (flat) bir adresleme \u015femas\u0131yla y\u00f6netir. Her nesne, verinin kendisi, benzersiz bir tan\u0131mlay\u0131c\u0131 (anahtar) ve iste\u011fe ba\u011fl\u0131 metadata (veri hakk\u0131nda bilgi) i\u00e7erir. DigitalOcean Spaces, Amazon S3, Google Cloud Storage ve Azure Blob Storage gibi hizmetler, bu teknolojinin pop\u00fcler \u00f6rnekleridir.<\/p>\n<p>Spaces gibi S3 uyumlu bir Object Storage hizmeti, \u00f6zellikle web uygulamalar\u0131nda dosya y\u00fckleme i\u00e7in idealdir \u00e7\u00fcnk\u00fc:<\/p>\n<ul>\n<li><strong>\u00d6l\u00e7eklenebilirlik:<\/strong> Milyonlarca hatta milyarlarca nesneyi depolayabilir ve depolama alan\u0131n\u0131z\u0131 gerekti\u011finde otomatik olarak geni\u015fletebilirsiniz. Bu, uygulaman\u0131z\u0131n b\u00fcy\u00fcmesiyle birlikte depolama altyap\u0131n\u0131z\u0131 endi\u015felenmeden \u00f6l\u00e7ekleyebilece\u011finiz anlam\u0131na gelir.<\/li>\n<li><strong>Dayan\u0131kl\u0131l\u0131k ve G\u00fcvenilirlik:<\/strong> Nesneler genellikle birden fazla fiziksel konumda \u00e7o\u011falt\u0131l\u0131r, bu da donan\u0131m ar\u0131zalar\u0131na kar\u015f\u0131 y\u00fcksek dayan\u0131kl\u0131l\u0131k sa\u011flar. Verilerinizin kaybolma riski olduk\u00e7a d\u00fc\u015f\u00fckt\u00fcr.<\/li>\n<li><strong>Maliyet Etkinli\u011fi:<\/strong> Kullan\u0131m ba\u015f\u0131na \u00f6deme modeli sayesinde, yaln\u0131zca depolad\u0131\u011f\u0131n\u0131z veri miktar\u0131 ve yapt\u0131\u011f\u0131n\u0131z istekler i\u00e7in \u00f6deme yapars\u0131n\u0131z. Bu, \u00f6zellikle b\u00fcy\u00fck miktarda veri depolayan ancak nadiren eri\u015filen durumlar i\u00e7in \u00e7ok ekonomiktir.<\/li>\n<li><strong>Eri\u015filebilirlik:<\/strong> Nesneler HTTP\/HTTPS \u00fczerinden URL&#8217;ler arac\u0131l\u0131\u011f\u0131yla eri\u015filebilir. Bu, do\u011frudan web taray\u0131c\u0131lar\u0131ndan veya di\u011fer uygulamalardan dosyalara kolayca eri\u015filmesini sa\u011flar. Ayr\u0131ca, \u00e7o\u011fu Object Storage hizmeti, CDN (\u0130\u00e7erik Da\u011f\u0131t\u0131m A\u011f\u0131) entegrasyonunu kolayla\u015ft\u0131rarak i\u00e7eri\u011fin d\u00fcnya \u00e7ap\u0131nda h\u0131zl\u0131 bir \u015fekilde da\u011f\u0131t\u0131lmas\u0131na yard\u0131mc\u0131 olur.<\/li>\n<li><strong>G\u00fcvenlik:<\/strong> Detayl\u0131 eri\u015fim kontrol politikalar\u0131 (ACL&#8217;ler, bucket politikalar\u0131, IAM rolleri) ile hangi kullan\u0131c\u0131lar\u0131n veya uygulamalar\u0131n hangi nesnelere eri\u015febilece\u011fini hassas bir \u015fekilde y\u00f6netebilirsiniz.<\/li>\n<\/ul>\n<p>Bir dosya Spaces&#8217;a y\u00fcklendi\u011finde, asl\u0131nda bir &#8220;bucket&#8221; (kova) ad\u0131 verilen bir konteynere bir &#8220;nesne&#8221; olarak kaydedilir. Her nesnenin benzersiz bir anahtar\u0131 (genellikle dosya ad\u0131) ve iste\u011fe ba\u011fl\u0131 olarak ek metadata&#8217;s\u0131 bulunur. \u00d6rne\u011fin, bir resim dosyas\u0131 y\u00fcklendi\u011finde, anahtar &#8220;profil-resmi-123.jpg&#8221; olabilir ve metadata &#8220;Content-Type: image\/jpeg&#8221; i\u00e7erebilir. Bu yap\u0131, Go ile GraphQL API&#8217;mizin dosyalar\u0131 depolama, y\u00f6netme ve eri\u015fme \u015feklini do\u011frudan etkileyecektir. Dolay\u0131s\u0131yla, bu hizmetleri anlamak, API&#8217;mizi do\u011fru bir \u015fekilde tasarlamak i\u00e7in temel bir ad\u0131md\u0131r. Nesne depolaman\u0131n bu esnek yap\u0131s\u0131, sadece dosya y\u00fckleme i\u00e7in de\u011fil, ayn\u0131 zamanda statik web sitelerinin bar\u0131nd\u0131r\u0131lmas\u0131, yedekleme ve ar\u015fivleme gibi bir\u00e7ok farkl\u0131 senaryo i\u00e7in de tercih edilmesini sa\u011flar. Bu sayede, uygulaman\u0131z\u0131n depolama gereksinimlerini gelece\u011fe y\u00f6nelik olarak da kar\u015f\u0131layabilirsiniz.<\/p>\n<h2>Go Projesini Ba\u015flatma ve Temel Ba\u011f\u0131ml\u0131l\u0131klar\u0131 Kurma<\/h2>\n<p>Go ile GraphQL API&#8217;mizi olu\u015fturmaya ba\u015flamak i\u00e7in \u00f6ncelikle yeni bir Go mod\u00fcl\u00fc ba\u015flatmam\u0131z ve gerekli ba\u011f\u0131ml\u0131l\u0131klar\u0131 eklememiz gerekiyor. Bu b\u00f6l\u00fcm, projenin temel iskeletini olu\u015fturma ad\u0131mlar\u0131n\u0131 i\u00e7ermektedir.<\/p>\n<h3>Go Mod\u00fcl\u00fcn\u00fc Ba\u015flatma ve Ba\u011f\u0131ml\u0131l\u0131klar\u0131 Ekleme<\/h3>\n<p>\u0130lk olarak, projeniz i\u00e7in bir dizin olu\u015fturun ve bu dizine gidin:<\/p>\n<pre><code class=\"language-bash\">\nmkdir go-graphql-upload\ncd go-graphql-upload\n<\/code><\/pre>\n<p>\u015eimdi, Go mod\u00fcl\u00fcn\u00fc ba\u015flat\u0131n. Bu komut, projenizin ba\u011f\u0131ml\u0131l\u0131klar\u0131n\u0131 y\u00f6netecek olan <code>go.mod<\/code> dosyas\u0131n\u0131 olu\u015fturacakt\u0131r:<\/p>\n<pre><code class=\"language-bash\">\ngo mod init github.com\/kullaniciadi\/go-graphql-upload\n<\/code><\/pre>\n<p><code>github.com\/kullaniciadi\/go-graphql-upload<\/code> yerine kendi GitHub kullan\u0131c\u0131 ad\u0131n\u0131z\u0131 veya tercih etti\u011finiz mod\u00fcl yolunu kullanabilirsiniz.<\/p>\n<p>Ard\u0131ndan, GraphQL sunucusu i\u00e7in <code>gqlgen<\/code> k\u00fct\u00fcphanesini ve S3 uyumlu Object Storage (Spaces) ile etkile\u015fim kurmak i\u00e7in AWS SDK for Go&#8217;yu (\u00f6zellikle S3 ve S3 Manager paketlerini) eklememiz gerekiyor. <code>gqlgen<\/code>, GraphQL \u015feman\u0131zdan Go kodunu otomatik olarak olu\u015fturan g\u00fc\u00e7l\u00fc bir ara\u00e7t\u0131r.<\/p>\n<pre><code class=\"language-bash\">\ngo get github.com\/99designs\/gqlgen\ngo get github.com\/aws\/aws-sdk-go\/aws\ngo get github.com\/aws\/aws-sdk-go\/aws\/session\ngo get github.com\/aws\/aws-sdk-go\/service\/s3\ngo get github.com\/aws\/aws-sdk-go\/service\/s3\/s3manager\ngo get github.com\/joho\/godotenv # Ortam de\u011fi\u015fkenlerini y\u00f6netmek i\u00e7in (opsiyonel ama \u00f6nerilir)\n<\/code><\/pre>\n<p>Bu komutlar, belirtilen k\u00fct\u00fcphaneleri projenize indirir ve <code>go.mod<\/code> dosyan\u0131za ekler. <code>godotenv<\/code>, yerel geli\u015ftirme ortam\u0131n\u0131zda API anahtarlar\u0131 gibi hassas bilgileri <code>.env<\/code> dosyas\u0131ndan y\u00fcklemek i\u00e7in kullan\u0131\u015fl\u0131d\u0131r.<\/p>\n<h3>gqlgen ile Temel Yap\u0131land\u0131rma<\/h3>\n<p><code>gqlgen<\/code>, projenizin GraphQL \u015femas\u0131n\u0131 temel alarak bir\u00e7ok boilerplate kodunu sizin i\u00e7in olu\u015fturur. Bu, geli\u015ftirme s\u00fcrecini h\u0131zland\u0131r\u0131r ve hatalar\u0131 azalt\u0131r. Temel yap\u0131land\u0131rmay\u0131 olu\u015fturmak i\u00e7in \u015fu komutu \u00e7al\u0131\u015ft\u0131r\u0131n:<\/p>\n<pre><code class=\"language-bash\">\ngo run github.com\/99designs\/gqlgen init\n<\/code><\/pre>\n<p>Bu komut, projenizin k\u00f6k dizininde a\u015fa\u011f\u0131daki dosyalar\u0131 olu\u015fturacakt\u0131r:<\/p>\n<ul>\n<li><code>gqlgen.yml<\/code>: <code>gqlgen<\/code> yap\u0131land\u0131rma dosyas\u0131. Giri\u015f \u015feman\u0131z\u0131, model ba\u011flamalar\u0131n\u0131 ve di\u011fer ayarlar\u0131 tan\u0131mlar.<\/li>\n<li><code>graph\/schema.graphqls<\/code>: GraphQL \u015feman\u0131z\u0131n tan\u0131mlanaca\u011f\u0131 dosya. Ba\u015flang\u0131\u00e7ta \u00f6rnek sorgu ve mutasyonlar\u0131 i\u00e7erecektir.<\/li>\n<li><code>graph\/resolver.go<\/code>: GraphQL resolver fonksiyonlar\u0131n\u0131z\u0131n yer alaca\u011f\u0131 dosya. Buras\u0131 i\u015f mant\u0131\u011f\u0131n\u0131z\u0131 yazaca\u011f\u0131n\u0131z yerdir.<\/li>\n<li><code>graph\/generated.go<\/code>: <code>gqlgen<\/code> taraf\u0131ndan \u015feman\u0131zdan otomatik olarak olu\u015fturulan kod. Bu dosyay\u0131 elle d\u00fczenlememelisiniz.<\/li>\n<li><code>server.go<\/code>: GraphQL sunucunuzu ba\u015flatan ana dosya.<\/li>\n<\/ul>\n<p>Bu ad\u0131mlarla, GraphQL API&#8217;nizi geli\u015ftirmeye ba\u015flamak i\u00e7in sa\u011flam bir temel olu\u015fturmu\u015f olursunuz. <code>gqlgen<\/code>&#8216;in otomatik kod olu\u015fturma yetene\u011fi, \u00f6zellikle b\u00fcy\u00fck ve karma\u015f\u0131k GraphQL \u015femalar\u0131yla \u00e7al\u0131\u015f\u0131rken zaman kazand\u0131r\u0131r. Art\u0131k <code>schema.graphqls<\/code> dosyam\u0131z\u0131 dosya y\u00fckleme i\u00e7in gerekli mutasyonlar\u0131 i\u00e7erecek \u015fekilde d\u00fczenleyebiliriz.<\/p>\n<h2>GraphQL \u015eemas\u0131n\u0131 Tan\u0131mlama: Dosya Y\u00fckleme Mutasyonu Nas\u0131l Olu\u015fturulur?<\/h2>\n<p>GraphQL \u015femas\u0131, API&#8217;nizin sundu\u011fu t\u00fcm veri t\u00fcrlerini, sorgular\u0131 (query) ve mutasyonlar\u0131 (mutation) tan\u0131mlayan merkezi bir s\u00f6zle\u015fmedir. Dosya y\u00fckleme i\u015flevini API&#8217;mize eklemek i\u00e7in \u00f6zel bir mutasyon tan\u0131mlamam\u0131z ve <code>Upload<\/code> scalar t\u00fcr\u00fcn\u00fc kullanmam\u0131z gerekecek. Bu b\u00f6l\u00fcm, <code>schema.graphqls<\/code> dosyas\u0131n\u0131 nas\u0131l d\u00fczenleyece\u011fimizi ad\u0131m ad\u0131m g\u00f6sterecektir.<\/p>\n<h3><code>schema.graphqls<\/code> Dosyas\u0131n\u0131n D\u00fczenlenmesi<\/h3>\n<p><code>gqlgen init<\/code> komutuyla olu\u015fturulan <code>graph\/schema.graphqls<\/code> dosyas\u0131n\u0131 a\u00e7\u0131n. \u0130\u00e7eri\u011fi muhtemelen \u015f\u00f6yle g\u00f6r\u00fcnecektir:<\/p>\n<pre><code class=\"language-graphql\">\n# GraphQL schema example\n#\n# https:\/\/gqlgen.com\/getting-started\/\n\ntype Todo {\n  id: ID!\n  text: String!\n  done: Boolean!\n}\n\ntype Query {\n  todos: [Todo!]!\n}\n\ntype Mutation {\n  createTodo(text: String!): Todo!\n}\n<\/code><\/pre>\n<p>Bu \u00f6rnek \u015femay\u0131 dosya y\u00fckleme mutasyonumuzu i\u00e7erecek \u015fekilde de\u011fi\u015ftirmemiz gerekiyor. \u00d6ncelikle, GraphQL&#8217;in dosya y\u00fckleme i\u00e7in \u00f6zel olarak tasarlanm\u0131\u015f <code>Upload<\/code> scalar t\u00fcr\u00fcn\u00fc tan\u0131mlamal\u0131y\u0131z. Bu scalar, <code>multipart\/form-data<\/code> iste\u011findeki dosya b\u00f6l\u00fcm\u00fcn\u00fc temsil eder. Ard\u0131ndan, bir dosya y\u00fckleme mutasyonu ve y\u00fcklenen dosya hakk\u0131nda bilgi d\u00f6nd\u00fcrecek bir <code>File<\/code> t\u00fcr\u00fc olu\u015fturaca\u011f\u0131z.<\/p>\n<p><code>graph\/schema.graphqls<\/code> dosyan\u0131z\u0131 a\u015fa\u011f\u0131daki gibi g\u00fcncelleyin:<\/p>\n<pre><code class=\"language-graphql\">\n# Dosya y\u00fcklemeleri i\u00e7in \u00f6zel scalar t\u00fcr\u00fc\nscalar Upload\n\n# Y\u00fcklenen dosya hakk\u0131nda bilgi d\u00f6nd\u00fcrecek tip\ntype File {\n  id: ID!\n  filename: String!\n  mimetype: String!\n  encoding: String!\n  url: String! # Dosyan\u0131n depoland\u0131\u011f\u0131 URL\n  size: Int! # Dosya boyutu (byte cinsinden)\n}\n\n# Sorgular (Queries) - \u015eimdilik bo\u015f b\u0131rakabiliriz veya ekleyebiliriz\ntype Query {\n  hello: String!\n}\n\n# Mutasyonlar (Mutations) - Dosya y\u00fckleme i\u015flevimizi buraya ekleyece\u011fiz\ntype Mutation {\n  # Tek bir dosya y\u00fcklemek i\u00e7in mutasyon\n  uploadFile(file: Upload!): File!\n  # Birden fazla dosya y\u00fcklemek i\u00e7in (iste\u011fe ba\u011fl\u0131)\n  uploadMultipleFiles(files: [Upload!]!): [File!]!\n}\n<\/code><\/pre>\n<p>Bu \u015fema tan\u0131m\u0131nda:<\/p>\n<ul>\n<li><code>scalar Upload<\/code>: GraphQL&#8217;e dosya y\u00fckleme yetene\u011fi kazand\u0131r\u0131r. <code>gqlgen<\/code> bu scalar&#8217;\u0131 otomatik olarak i\u015fleyecektir.<\/li>\n<li><code>type File<\/code>: Y\u00fcklenen bir dosya hakk\u0131nda istemciye geri d\u00f6nd\u00fcr\u00fclecek bilgileri tan\u0131mlar. <code>id<\/code>, <code>filename<\/code>, <code>mimetype<\/code>, <code>encoding<\/code>, <code>url<\/code> ve <code>size<\/code> gibi alanlar i\u00e7erir. <code>url<\/code> alan\u0131, dosyan\u0131n Object Storage&#8217;da depoland\u0131\u011f\u0131 genel eri\u015filebilir URL&#8217;yi tutacakt\u0131r.<\/li>\n<li><code>type Mutation<\/code>: API&#8217;mizin veri \u00fczerinde de\u011fi\u015fiklik yapma yetene\u011fini tan\u0131mlar.\n<ul>\n<li><code>uploadFile(file: Upload!): File!<\/code>: Tek bir dosya y\u00fcklemek i\u00e7in bir mutasyon. <code>file<\/code> arg\u00fcman\u0131 zorunlu bir <code>Upload<\/code> t\u00fcr\u00fcndedir ve mutasyon ba\u015far\u0131l\u0131 olursa bir <code>File<\/code> nesnesi d\u00f6nd\u00fcr\u00fcr.<\/li>\n<li><code>uploadMultipleFiles(files: [Upload!]!): [File!]!<\/code>: \u0130ste\u011fe ba\u011fl\u0131 olarak, birden fazla dosyay\u0131 ayn\u0131 anda y\u00fcklemek i\u00e7in bir mutasyon. <code>files<\/code> arg\u00fcman\u0131 bir <code>Upload<\/code> dizisi al\u0131r ve bir <code>File<\/code> dizisi d\u00f6nd\u00fcr\u00fcr.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3>\u015eema De\u011fi\u015fikliklerini Uygulama<\/h3>\n<p><code>schema.graphqls<\/code> dosyas\u0131n\u0131 g\u00fcncelledikten sonra, <code>gqlgen<\/code>&#8216;in bu de\u011fi\u015fiklikleri Go koduna yans\u0131tmas\u0131 i\u00e7in yeniden \u00e7al\u0131\u015ft\u0131rmam\u0131z gerekir:<\/p>\n<pre><code class=\"language-bash\">\ngo run github.com\/99designs\/gqlgen generate\n<\/code><\/pre>\n<p>Bu komut, <code>graph\/generated.go<\/code> dosyas\u0131n\u0131 ve <code>graph\/models_gen.go<\/code> dosyas\u0131n\u0131 g\u00fcncelleyecektir. <code>models_gen.go<\/code> dosyas\u0131, \u015femada tan\u0131mlad\u0131\u011f\u0131m\u0131z <code>File<\/code> t\u00fcr\u00fc i\u00e7in Go yap\u0131lar\u0131n\u0131 (struct) i\u00e7erecektir. \u00d6rne\u011fin, <code>File<\/code> t\u00fcr\u00fc i\u00e7in \u015f\u00f6yle bir yap\u0131 olu\u015fturulacakt\u0131r:<\/p>\n<pre><code class=\"language-go\">\n\/\/ models_gen.go i\u00e7inde olu\u015facak yap\u0131ya benzer\ntype File struct {\n\tID       string <code class=\"language-\">json:\"id\"<\/code>\n\tFilename string <code class=\"language-\">json:\"filename\"<\/code>\n\tMimetype string <code class=\"language-\">json:\"mimetype\"<\/code>\n\tEncoding string <code class=\"language-\">json:\"encoding\"<\/code>\n\tURL      string <code class=\"language-\">json:\"url\"<\/code>\n\tSize     int    <code class=\"language-\">json:\"size\"<\/code>\n}\n<\/code><\/pre>\n<p>Art\u0131k GraphQL \u015femam\u0131z dosya y\u00fcklemeyi destekleyecek \u015fekilde haz\u0131r. Bir sonraki ad\u0131m, bu mutasyon i\u00e7in Go taraf\u0131nda resolver fonksiyonlar\u0131n\u0131 ve dosya i\u015fleme mant\u0131\u011f\u0131n\u0131 geli\u015ftirmektir. Bu, sunucumuzun gelen dosyalar\u0131 nas\u0131l alaca\u011f\u0131n\u0131, i\u015fleyece\u011fini ve Object Storage&#8217;a nas\u0131l g\u00f6nderece\u011fini belirleyece\u011fimiz yerdir.<\/p>\n<h2>Go Sunucusu ve Resolver Fonksiyonlar\u0131n\u0131 Geli\u015ftirme: Dosya \u0130\u015fleme Mant\u0131\u011f\u0131<\/h2>\n<p>\u015eemam\u0131z\u0131 tan\u0131mlad\u0131ktan ve <code>gqlgen<\/code> ile gerekli Go kodlar\u0131n\u0131 olu\u015fturduktan sonra, s\u0131ra dosya y\u00fckleme mutasyonumuz i\u00e7in ger\u00e7ek i\u015f mant\u0131\u011f\u0131n\u0131 yazmaya geldi. Bu, <code>resolver.go<\/code> dosyas\u0131nda ger\u00e7ekle\u015fecek ve gelen dosyay\u0131 i\u015fleyip Object Storage&#8217;a g\u00f6nderme ad\u0131mlar\u0131n\u0131 i\u00e7erecektir. Ayr\u0131ca, GraphQL sunucumuzu ba\u015flatan <code>server.go<\/code> dosyas\u0131nda baz\u0131 ayarlamalar yapaca\u011f\u0131z.<\/p>\n<h3><code>server.go<\/code> Dosyas\u0131n\u0131n Yap\u0131land\u0131r\u0131lmas\u0131<\/h3>\n<p><code>gqlgen init<\/code> komutuyla olu\u015fturulan <code>server.go<\/code> dosyas\u0131, temel bir GraphQL sunucusu ba\u015flat\u0131r. Dosya y\u00fckleme deste\u011fi i\u00e7in, <code>gqlgen<\/code>&#8216;in <code>Upload<\/code> scalar&#8217;\u0131n\u0131 do\u011fru \u015fekilde i\u015flemesi i\u00e7in baz\u0131 middleware&#8217;lara ihtiyac\u0131m\u0131z olabilir. Ancak <code>gqlgen<\/code>&#8216;in kendisi, <code>multipart\/form-data<\/code> isteklerini ve <code>Upload<\/code> scalar&#8217;\u0131n\u0131 yerle\u015fik olarak destekler, bu nedenle genellikle ek bir middleware&#8217;a gerek kalmaz.<\/p>\n<p><code>server.go<\/code> dosyan\u0131z\u0131n genel yap\u0131s\u0131 \u015f\u00f6yle olmal\u0131d\u0131r:<\/p>\n<pre><code class=\"language-go\">\npackage main\n\nimport (\n\t\"log\"\n\t\"net\/http\"\n\t\"os\"\n\n\t\"github.com\/99designs\/gqlgen\/graphql\/handler\"\n\t\"github.com\/99designs\/gqlgen\/graphql\/playground\"\n\t\"github.com\/kullaniciadi\/go-graphql-upload\/graph\"\n\t\"github.com\/kullaniciadi\/go-graphql-upload\/graph\/generated\"\n\t\"github.com\/joho\/godotenv\" \/\/ Ortam de\u011fi\u015fkenleri i\u00e7in\n)\n\nconst defaultPort = \"8080\"\n\nfunc main() {\n\t\/\/ .env dosyas\u0131ndan ortam de\u011fi\u015fkenlerini y\u00fckle\n\terr := godotenv.Load()\n\tif err != nil {\n\t\tlog.Println(\"Error loading .env file, assuming environment variables are set\")\n\t}\n\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = defaultPort\n\t}\n\n\tsrv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}}))\n\n\thttp.Handle(\"\/\", playground.Handler(\"GraphQL playground\", \"\/query\"))\n\thttp.Handle(\"\/query\", srv)\n\n\tlog.Printf(\"connect to http:\/\/localhost:%s\/ for GraphQL playground\", port)\n\tlog.Fatal(http.ListenAndServe(\":\"+port, nil))\n}\n<\/code><\/pre>\n<p>Burada \u00f6nemli olan, <code>generated.Config{Resolvers: &graph.Resolver{}}<\/code> k\u0131sm\u0131d\u0131r. Bu, <code>graph\/resolver.go<\/code> dosyas\u0131nda tan\u0131mlayaca\u011f\u0131m\u0131z resolver&#8217;lar\u0131m\u0131z\u0131n sunucuya ba\u011flanmas\u0131n\u0131 sa\u011flar.<\/p>\n<h3><code>resolver.go<\/code> Dosyas\u0131nda Dosya Y\u00fckleme Mant\u0131\u011f\u0131<\/h3>\n<p><code>graph\/resolver.go<\/code> dosyas\u0131, GraphQL \u015femam\u0131zdaki her alan i\u00e7in ger\u00e7ek i\u015f mant\u0131\u011f\u0131n\u0131 i\u00e7erir. <code>gqlgen generate<\/code> komutunu \u00e7al\u0131\u015ft\u0131rd\u0131ktan sonra, bu dosyada <code>UploadFile<\/code> ve <code>UploadMultipleFiles<\/code> mutasyonlar\u0131 i\u00e7in placeholder fonksiyonlar g\u00f6receksiniz. \u015eimdi bu fonksiyonlar\u0131, gelen dosyay\u0131 i\u015fleyecek ve Object Storage&#8217;a y\u00fckleyecek \u015fekilde doldural\u0131m.<\/p>\n<p>\u00d6ncelikle, <code>Resolver<\/code> yap\u0131m\u0131za Object Storage istemcisini eklememiz gerekiyor. Bu, S3 uyumlu bir servise (DigitalOcean Spaces gibi) ba\u011flanmak i\u00e7in kullan\u0131lacak.<\/p>\n<pre><code class=\"language-go\">\npackage graph\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"mime\"\n\t\"path\/filepath\"\n\t\"os\"\n\n\t\"github.com\/99designs\/gqlgen\/graphql\"\n\t\"github.com\/aws\/aws-sdk-go\/aws\"\n\t\"github.com\/aws\/aws-sdk-go\/aws\/session\"\n\t\"github.com\/aws\/aws-sdk-go\/service\/s3\/s3manager\"\n\t\"github.com\/google\/uuid\" \/\/ Benzersiz ID'ler i\u00e7in\n\t\"github.com\/kullaniciadi\/go-graphql-upload\/graph\/generated\"\n\t\"github.com\/kullaniciadi\/go-graphql-upload\/graph\/model\"\n)\n\n\/\/ This file will not be regenerated automatically.\n\/\/\n\/\/ It serves as dependency injection for your app, add any dependencies you require here.\n\ntype Resolver struct {\n\tUploader *s3manager.Uploader\n\tBucketName string\n\tRegion string\n\tEndpoint string\n}\n\n\/\/ Resolver yap\u0131m\u0131z\u0131n ba\u015flang\u0131\u00e7ta nas\u0131l olu\u015fturulaca\u011f\u0131n\u0131 g\u00f6steren \u00f6rnek.\n\/\/ main.go'da \u00e7a\u011fr\u0131lacak.\nfunc NewResolver() *Resolver {\n\t\/\/ AWS SDK oturumunu ba\u015flat\n\tsess := session.Must(session.NewSession(&aws.Config{\n\t\tRegion:   aws.String(os.Getenv(\"AWS_REGION\")),\n\t\tEndpoint: aws.String(os.Getenv(\"AWS_ENDPOINT\")), \/\/ DigitalOcean Spaces i\u00e7in gerekli\n\t}))\n\n\t\/\/ S3 Uploader olu\u015ftur\n\tuploader := s3manager.NewUploader(sess)\n\n\treturn &Resolver{\n\t\tUploader: uploader,\n\t\tBucketName: os.Getenv(\"S3_BUCKET_NAME\"),\n\t\tRegion: os.Getenv(\"AWS_REGION\"),\n\t\tEndpoint: os.Getenv(\"AWS_ENDPOINT\"),\n\t}\n}\n\n\/\/ Mutation returns generated.MutationResolver implementation.\nfunc (r *Resolver) Mutation() generated.MutationResolver { return &mutationResolver{r} }\n\n\/\/ Query returns generated.QueryResolver implementation.\nfunc (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} }\n\ntype mutationResolver struct{ *Resolver }\ntype queryResolver struct{ *Resolver }\n\nfunc (r *queryResolver) Hello(ctx context.Context) (string, error) {\n\treturn \"Merhaba, GraphQL Dosya Y\u00fckleme API'sine ho\u015f geldiniz!\", nil\n}\n\nfunc (r *mutationResolver) UploadFile(ctx context.Context, file graphql.Upload) (*model.File, error) {\n\tlog.Printf(\"Y\u00fckleme iste\u011fi al\u0131nd\u0131: %s, %s, %d bytes\", file.Filename, file.ContentType, file.Size)\n\n\t\/\/ Benzersiz bir dosya ad\u0131 olu\u015ftur\n\tfileExt := filepath.Ext(file.Filename)\n\tuniqueFilename := fmt.Sprintf(\"%s%s\", uuid.New().String(), fileExt)\n\tobjectKey := \"uploads\/\" + uniqueFilename \/\/ Spaces i\u00e7inde bir klas\u00f6re kaydet\n\n\t\/\/ Dosyay\u0131 Spaces'a y\u00fckle\n\tuploadInput := &s3manager.UploadInput{\n\t\tBucket: aws.String(r.BucketName),\n\t\tKey:    aws.String(objectKey),\n\t\tBody:   file.File, \/\/ graphql.Upload'\u0131n io.Reader'\u0131\n\t\tContentType: aws.String(file.ContentType),\n\t}\n\n\tresult, err := r.Uploader.Upload(uploadInput)\n\tif err != nil {\n\t\tlog.Printf(\"Dosya y\u00fcklenirken hata olu\u015ftu: %v\", err)\n\t\treturn nil, fmt.Errorf(\"dosya y\u00fcklenemedi: %w\", err)\n\t}\n\n\tlog.Printf(\"Dosya ba\u015far\u0131yla y\u00fcklendi: %s\", *result.Location)\n\n\t\/\/ Y\u00fcklenen dosya bilgisini d\u00f6nd\u00fcr\n\treturn &model.File{\n\t\tID:       uuid.New().String(), \/\/ Veya Spaces'tan gelen bir ID kullanabilirsiniz\n\t\tFilename: file.Filename,\n\t\tMimetype: file.ContentType,\n\t\tEncoding: \"binary\", \/\/ GraphQL'in 'Upload' scalar'\u0131 genellikle binary'dir\n\t\tURL:      *result.Location,\n\t\tSize:     int(file.Size),\n\t}, nil\n}\n\nfunc (r *mutationResolver) UploadMultipleFiles(ctx context.Context, files []*graphql.Upload) ([]*model.File, error) {\n\tvar uploadedFiles []*model.File\n\tfor _, file := range files {\n\t\tuploadedFile, err := r.UploadFile(ctx, *file) \/\/ Tek dosya y\u00fckleme fonksiyonunu tekrar kullan\n\t\tif err != nil {\n\t\t\t\/\/ Hata durumunda di\u011fer dosyalar\u0131n y\u00fcklemesini durdurmak isteyebilirsiniz\n\t\t\t\/\/ veya hatalar\u0131 toplay\u0131p t\u00fcm\u00fcn\u00fc d\u00f6nd\u00fcrebilirsiniz.\n\t\t\tlog.Printf(\"Birden fazla dosya y\u00fcklenirken hata olu\u015ftu: %v\", err)\n\t\t\treturn nil, fmt.Errorf(\"dosyalardan biri y\u00fcklenemedi: %w\", err)\n\t\t}\n\t\tuploadedFiles = append(uploadedFiles, uploadedFile)\n\t}\n\treturn uploadedFiles, nil\n}\n<\/code><\/pre>\n<p><code>main.go<\/code> dosyas\u0131n\u0131 da <code>NewResolver()<\/code> fonksiyonunu \u00e7a\u011f\u0131racak \u015fekilde g\u00fcncellemeniz gerekiyor:<\/p>\n<pre><code class=\"language-go\">\n\/\/ main.go dosyas\u0131nda de\u011fi\u015fiklik\n\/\/ ...\nfunc main() {\n\t\/\/ ... (godotenv.Load ve port ayarlar\u0131) ...\n\n\t\/\/ Resolver'\u0131 ba\u015flat ve S3 Uploader'\u0131 enjekte et\n\tresolver := graph.NewResolver()\n\n\tsrv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: resolver}))\n\n\t\/\/ ... (http.Handle ve log ayarlar\u0131) ...\n}\n<\/code><\/pre>\n<p>Bu kodda:<\/p>\n<ul>\n<li><code>Resolver<\/code> yap\u0131s\u0131na <code>Uploader<\/code>, <code>BucketName<\/code>, <code>Region<\/code> ve <code>Endpoint<\/code> eklendi. Bu, resolver fonksiyonlar\u0131n\u0131n Object Storage ile etkile\u015fime ge\u00e7mesini sa\u011flar.<\/li>\n<li><code>NewResolver()<\/code> fonksiyonu, AWS SDK oturumunu ve <code>s3manager.Uploader<\/code>&#8216;\u0131 ba\u015flat\u0131r. Bu fonksiyon, <code>main.go<\/code> i\u00e7inde \u00e7a\u011fr\u0131larak resolver&#8217;a ba\u011f\u0131ml\u0131l\u0131k enjeksiyonu sa\u011flar. Ortam de\u011fi\u015fkenlerinden (<code>AWS_REGION<\/code>, <code>AWS_ENDPOINT<\/code>, <code>S3_BUCKET_NAME<\/code>) gerekli bilgileri al\u0131r.<\/li>\n<li><code>UploadFile<\/code> mutasyonunda:\n<ul>\n<li><code>graphql.Upload<\/code> t\u00fcr\u00fcnden gelen dosya (<code>file.File<\/code>), <code>io.Reader<\/code> aray\u00fcz\u00fcn\u00fc uygular, bu da onu do\u011frudan S3 uploader&#8217;\u0131na beslememizi sa\u011flar.<\/li>\n<li><code>uuid.New().String()<\/code> ile benzersiz bir dosya ad\u0131 olu\u015fturulur ve dosyan\u0131n orijinal uzant\u0131s\u0131 korunur. Bu, adland\u0131rma \u00e7ak\u0131\u015fmalar\u0131n\u0131 \u00f6nler.<\/li>\n<li><code>s3manager.Uploader<\/code> kullan\u0131larak dosya Object Storage&#8217;a y\u00fcklenir. <code>UploadInput<\/code> nesnesine bucket ad\u0131, anahtar (dosya yolu), dosya i\u00e7eri\u011fi ve i\u00e7erik t\u00fcr\u00fc bilgileri eklenir.<\/li>\n<li>Y\u00fckleme ba\u015far\u0131l\u0131 olursa, Object Storage&#8217;dan d\u00f6nen konum (URL) ve di\u011fer dosya bilgileri ile bir <code>model.File<\/code> nesnesi olu\u015fturulur ve istemciye d\u00f6nd\u00fcr\u00fcl\u00fcr.<\/li>\n<\/ul>\n<\/li>\n<li><code>UploadMultipleFiles<\/code> mutasyonu, tek dosya y\u00fckleme fonksiyonunu her bir dosya i\u00e7in tekrar kullanarak birden fazla dosyan\u0131n y\u00fcklenmesini sa\u011flar.<\/li>\n<\/ul>\n<p>Bu sayede, GraphQL API&#8217;miz art\u0131k gelen dosya y\u00fckleme isteklerini i\u015fleyebilir, dosyalar\u0131 benzersiz bir \u015fekilde adland\u0131rabilir ve g\u00fcvenli bir \u015fekilde Object Storage&#8217;a depolayabilir. Sonraki ad\u0131m, bu entegrasyonu detayland\u0131rmak ve Object Storage servisleriyle nas\u0131l daha verimli \u00e7al\u0131\u015fabilece\u011fimizi g\u00f6rmektir.<\/p>\n<h2>Spaces (S3 Uyumlu Object Storage) ile Entegrasyon: Dosyalar\u0131 Y\u00fckleme<\/h2>\n<p>\u00d6nceki b\u00f6l\u00fcmde, Go resolver&#8217;\u0131m\u0131z\u0131 kullanarak dosya y\u00fckleme mant\u0131\u011f\u0131n\u0131 tasla\u011f\u0131n\u0131 olu\u015fturduk. \u015eimdi bu mant\u0131\u011f\u0131 S3 uyumlu bir Object Storage hizmeti olan DigitalOcean Spaces ile tam olarak entegre edelim. AWS SDK for Go, S3 API&#8217;si ile uyumlu t\u00fcm hizmetlerle \u00e7al\u0131\u015fabildi\u011fi i\u00e7in, Spaces ile etkile\u015fim kurmak olduk\u00e7a kolayd\u0131r. Temel olarak, SDK&#8217;y\u0131 Spaces&#8217;\u0131n endpoint&#8217;ini g\u00f6sterecek \u015fekilde yap\u0131land\u0131rmam\u0131z yeterlidir.<\/p>\n<h3>AWS SDK&#8217;s\u0131n\u0131 Spaces \u0130\u00e7in Yap\u0131land\u0131rma<\/h3>\n<p>AWS SDK&#8217;s\u0131n\u0131 DigitalOcean Spaces gibi S3 uyumlu bir hizmetle kullanmak i\u00e7in, oturum yap\u0131land\u0131rmas\u0131nda <code>Endpoint<\/code> parametresini belirtmeniz gerekir. Ayr\u0131ca, Spaces eri\u015fim anahtarlar\u0131n\u0131z\u0131 (Access Key ve Secret Key) ortam de\u011fi\u015fkenleri arac\u0131l\u0131\u011f\u0131yla sa\u011flaman\u0131z \u00f6nemlidir.<\/p>\n<p><code>.env<\/code> dosyan\u0131zda a\u015fa\u011f\u0131daki de\u011fi\u015fkenleri tan\u0131mlad\u0131\u011f\u0131n\u0131zdan emin olun:<\/p>\n<pre><code class=\"language-ini\">\n# .env\nAWS_REGION=fra1 # DigitalOcean Spaces b\u00f6lgeniz (\u00f6rn: nyc3, fra1)\nAWS_ENDPOINT=https:\/\/fra1.digitaloceanspaces.com # Spaces endpoint URL'si\nS3_BUCKET_NAME=your-space-name # Olu\u015fturdu\u011funuz Spaces'\u0131n ad\u0131\nAWS_ACCESS_KEY_ID=YOUR_DO_SPACES_ACCESS_KEY # DigitalOcean Spaces Eri\u015fim Anahtar\u0131n\u0131z\nAWS_SECRET_ACCESS_KEY=YOUR_DO_SPACES_SECRET_KEY # DigitalOcean Spaces Gizli Anahtar\u0131n\u0131z\n<\/code><\/pre>\n<p><code>graph\/resolver.go<\/code> dosyas\u0131ndaki <code>NewResolver()<\/code> fonksiyonu, bu ortam de\u011fi\u015fkenlerini kullanarak bir AWS SDK oturumu ba\u015flatacakt\u0131r:<\/p>\n<pre><code class=\"language-go\">\n\/\/ graph\/resolver.go i\u00e7erisindeki NewResolver fonksiyonu\nfunc NewResolver() *Resolver {\n\t\/\/ AWS SDK oturumunu ba\u015flat\n\tsess := session.Must(session.NewSession(&aws.Config{\n\t\tRegion:   aws.String(os.Getenv(\"AWS_REGION\")),\n\t\tEndpoint: aws.String(os.Getenv(\"AWS_ENDPOINT\")), \/\/ Spaces i\u00e7in \u00f6nemli!\n\t\t\/\/ Kimlik bilgileri varsay\u0131lan olarak ortam de\u011fi\u015fkenlerinden (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) al\u0131n\u0131r.\n\t}))\n\n\t\/\/ S3 Uploader olu\u015ftur\n\tuploader := s3manager.NewUploader(sess)\n\n\treturn &Resolver{\n\t\tUploader: uploader,\n\t\tBucketName: os.Getenv(\"S3_BUCKET_NAME\"),\n\t\tRegion: os.Getenv(\"AWS_REGION\"),\n\t\tEndpoint: os.Getenv(\"AWS_ENDPOINT\"),\n\t}\n}\n<\/code><\/pre>\n<p>Bu yap\u0131land\u0131rma ile <code>uploader<\/code> nesnesi art\u0131k DigitalOcean Spaces&#8217;a dosya y\u00fcklemek i\u00e7in haz\u0131r hale gelmi\u015ftir. Kimlik bilgileri (Access Key ID ve Secret Access Key) varsay\u0131lan olarak ortam de\u011fi\u015fkenlerinden (<code>AWS_ACCESS_KEY_ID<\/code> ve <code>AWS_SECRET_ACCESS_KEY<\/code>) al\u0131n\u0131r, bu da kod i\u00e7inde hassas bilgileri saklamaktan ka\u00e7\u0131nmam\u0131z\u0131 sa\u011flar.<\/p>\n<h3>Dosyalar\u0131 Spaces&#8217;a Y\u00fckleme Mekanizmas\u0131<\/h3>\n<p><code>UploadFile<\/code> resolver fonksiyonumuzun kalbi, <code>s3manager.Uploader<\/code>&#8216;\u0131n <code>Upload<\/code> metodunu kullanmakt\u0131r. Bu metod, b\u00fcy\u00fck dosyalar\u0131 otomatik olarak par\u00e7alara ay\u0131r\u0131p \u00e7ok par\u00e7al\u0131 y\u00fckleme (multipart upload) yaparak performans\u0131 ve g\u00fcvenilirli\u011fi art\u0131r\u0131r.<\/p>\n<pre><code class=\"language-go\">\n\/\/ graph\/resolver.go i\u00e7erisindeki UploadFile fonksiyonundan ilgili k\u0131s\u0131m\nfunc (r *mutationResolver) UploadFile(ctx context.Context, file graphql.Upload) (*model.File, error) {\n\t\/\/ ... (Benzersiz dosya ad\u0131 olu\u015fturma) ...\n\tobjectKey := \"uploads\/\" + uniqueFilename \/\/ Spaces i\u00e7inde bir 'uploads' klas\u00f6r\u00fcne kaydetmek i\u00e7in\n\n\t\/\/ Dosyay\u0131 Spaces'a y\u00fckle\n\tuploadInput := &s3manager.UploadInput{\n\t\tBucket: aws.String(r.BucketName),\n\t\tKey:    aws.String(objectKey),\n\t\tBody:   file.File, \/\/ graphql.Upload'\u0131n io.Reader'\u0131\n\t\tContentType: aws.String(file.ContentType),\n\t\tACL:    aws.String(\"public-read\"), \/\/ Dosyan\u0131n genel eri\u015filebilir olmas\u0131n\u0131 sa\u011flar\n\t}\n\n\tresult, err := r.Uploader.Upload(uploadInput)\n\tif err != nil {\n\t\tlog.Printf(\"Dosya y\u00fcklenirken hata olu\u015ftu: %v\", err)\n\t\treturn nil, fmt.Errorf(\"dosya y\u00fcklenemedi: %w\", err)\n\t}\n\n\tlog.Printf(\"Dosya ba\u015far\u0131yla y\u00fcklendi: %s\", *result.Location)\n\n\t\/\/ Y\u00fcklenen dosya bilgisini d\u00f6nd\u00fcr\n\treturn &model.File{\n\t\tID:       uuid.New().String(),\n\t\tFilename: file.Filename,\n\t\tMimetype: file.ContentType,\n\t\tEncoding: \"binary\",\n\t\tURL:      *result.Location, \/\/ Y\u00fcklenen dosyan\u0131n URL'si\n\t\tSize:     int(file.Size),\n\t}, nil\n}\n<\/code><\/pre>\n<p>Burada ekledi\u011fimiz \u00f6nemli bir detay, <code>ACL: aws.String(\"public-read\")<\/code> sat\u0131r\u0131d\u0131r. Bu, y\u00fcklenen dosyan\u0131n herkes taraf\u0131ndan okunabilir olmas\u0131n\u0131 sa\u011flar. E\u011fer dosyalar\u0131n genel eri\u015filebilir olmas\u0131n\u0131 istemiyorsan\u0131z, bu sat\u0131r\u0131 kald\u0131rabilir ve eri\u015fimi Spaces bucket politikalar\u0131 veya \u00f6n imzal\u0131 URL&#8217;ler (presigned URLs) arac\u0131l\u0131\u011f\u0131yla y\u00f6netebilirsiniz. <code>result.Location<\/code>, y\u00fcklenen dosyan\u0131n tam URL&#8217;sini i\u00e7erir, bu da istemciye dosyan\u0131n nerede bulundu\u011funu bildirmek i\u00e7in m\u00fckemmeldir. Bu entegrasyon ad\u0131mlar\u0131 sayesinde, Go GraphQL API&#8217;niz art\u0131k DigitalOcean Spaces gibi S3 uyumlu herhangi bir Object Storage hizmetine g\u00fcvenilir ve verimli bir \u015fekilde dosya y\u00fckleyebilir. Bu, uygulaman\u0131z\u0131n depolama ihtiya\u00e7lar\u0131 i\u00e7in sa\u011flam bir temel olu\u015fturur ve gelecekteki \u00f6l\u00e7eklenebilirlik gereksinimlerini kar\u015f\u0131laman\u0131za yard\u0131mc\u0131 olur. Art\u0131k API&#8217;miz, dosyalar\u0131 al\u0131p bulut depolamaya g\u00fcvenle aktarabilir bir durumda.<\/p>\n<h2>\u0130leri D\u00fczey Konular: B\u00fcy\u00fck Dosya Y\u00fcklemeleri ve G\u00fcvenlik \u0130pu\u00e7lar\u0131<\/h2>\n<p>Dosya y\u00fckleme API&#8217;leri olu\u015ftururken, b\u00fcy\u00fck dosyalar\u0131n y\u00f6netimi ve g\u00fcvenlik en \u00f6nemli konulardan ikisidir. Bu b\u00f6l\u00fcmde, bu zorluklar\u0131n \u00fcstesinden gelmek i\u00e7in baz\u0131 ileri d\u00fczey teknikleri ve en iyi uygulamalar\u0131 inceleyece\u011fiz.<\/p>\n<h3>B\u00fcy\u00fck Dosya Y\u00fcklemeleri \u0130\u00e7in Optimizasyonlar<\/h3>\n<p>K\u00fc\u00e7\u00fck resimler veya belgeler i\u00e7in do\u011frudan y\u00fckleme y\u00f6ntemi yeterli olsa da, gigabaytlarca b\u00fcy\u00fckl\u00fckteki dosyalar\u0131 y\u00fcklerken performans\u0131 ve g\u00fcvenilirli\u011fi art\u0131rmak i\u00e7in farkl\u0131 stratejiler uygulamak gerekir.<\/p>\n<ol>\n<li><strong>\u00d6n \u0130mzal\u0131 URL&#8217;ler (Presigned URLs) ile Do\u011frudan Y\u00fckleme:<\/strong>\n<p>Bu, b\u00fcy\u00fck dosyalar\u0131 y\u00fcklemek i\u00e7in en yayg\u0131n ve \u00f6nerilen y\u00f6ntemlerden biridir. Mant\u0131k \u015f\u00f6yledir: \u0130stemci, API&#8217;nize bir dosya y\u00fcklemek istedi\u011fini belirtir (genellikle dosya ad\u0131 ve t\u00fcr\u00fc ile). API&#8217;niz, Spaces&#8217;tan belirli bir s\u00fcre ge\u00e7erli olacak, do\u011frudan o dosyay\u0131 y\u00fckleyebilece\u011fi bir &#8220;\u00f6n imzal\u0131 URL&#8221; (PUT iste\u011fi i\u00e7in) veya &#8220;\u00f6n imzal\u0131 form alan\u0131&#8221; (POST iste\u011fi i\u00e7in) ister. API, bu URL&#8217;yi istemciye geri d\u00f6nd\u00fcr\u00fcr. \u0130stemci daha sonra dosyay\u0131 do\u011frudan Spaces&#8217;a, API&#8217;nizin \u00fczerinden ge\u00e7meden y\u00fckler. Bu yakla\u015f\u0131m\u0131n faydalar\u0131:<\/p>\n<ul>\n<li><strong>Sunucu Y\u00fck\u00fcn\u00fc Azaltma:<\/strong> Dosya i\u00e7eri\u011fi API sunucunuz \u00fczerinden akmad\u0131\u011f\u0131 i\u00e7in, sunucunuzun CPU, bellek ve a\u011f bant geni\u015fli\u011fi \u00fczerindeki y\u00fck\u00fc \u00f6nemli \u00f6l\u00e7\u00fcde azal\u0131r.<\/li>\n<li><strong>Daha H\u0131zl\u0131 Y\u00fcklemeler:<\/strong> \u0130stemci, dosyay\u0131 do\u011frudan Object Storage&#8217;a y\u00fckledi\u011fi i\u00e7in, API sunucunuzun co\u011frafi konumundan ba\u011f\u0131ms\u0131z olarak daha d\u00fc\u015f\u00fck gecikmeyle daha h\u0131zl\u0131 y\u00fckleme deneyimi ya\u015fayabilir.<\/li>\n<li><strong>Geli\u015fmi\u015f G\u00fcvenlik:<\/strong> API, yaln\u0131zca belirli bir dosya i\u00e7in belirli bir s\u00fcre ge\u00e7erli olan imzal\u0131 bir URL olu\u015fturur. Bu, yetkisiz eri\u015fimi s\u0131n\u0131rlar.<\/li>\n<\/ul>\n<p>Go ile \u00f6n imzal\u0131 URL olu\u015fturma, AWS SDK&#8217;n\u0131n <code>s3.PresignRequest<\/code> veya <code>s3.PutObjectRequest<\/code> metodlar\u0131 kullan\u0131larak yap\u0131labilir. \u0130stemciye d\u00f6nd\u00fcr\u00fclen URL&#8217;yi kullanarak dosya y\u00fcklemesi tamamland\u0131ktan sonra, istemci API&#8217;nize bir bildirim g\u00f6nderebilir ve dosyan\u0131n Object Storage&#8217;a ba\u015far\u0131yla y\u00fcklendi\u011fini do\u011frulayabilir.<\/p>\n<\/li>\n<li><strong>\u00c7ok Par\u00e7al\u0131 Y\u00fckleme (Multipart Upload) ve Chunking:<\/strong>\n<p><code>s3manager.Uploader<\/code> zaten bu mekanizmay\u0131 dahili olarak kullan\u0131r, ancak istemci taraf\u0131nda da dosyalar\u0131 par\u00e7alara ay\u0131r\u0131p paralel olarak y\u00fcklemek, \u00f6zellikle zay\u0131f a\u011f ba\u011flant\u0131lar\u0131nda veya \u00e7ok b\u00fcy\u00fck dosyalarda faydal\u0131 olabilir. \u0130stemci, dosyay\u0131 k\u00fc\u00e7\u00fck &#8220;par\u00e7alara&#8221; (chunks) b\u00f6ler ve her bir par\u00e7ay\u0131 ayr\u0131 ayr\u0131 y\u00fckler. API&#8217;niz veya do\u011frudan Object Storage, bu par\u00e7alar\u0131 daha sonra birle\u015ftirir. Bu, ba\u015far\u0131s\u0131z olan par\u00e7alar\u0131n yeniden denemesini kolayla\u015ft\u0131r\u0131r ve y\u00fckleme i\u015flemini daha dayan\u0131kl\u0131 hale getirir.<\/p>\n<\/li>\n<\/ol>\n<h3>G\u00fcvenlik \u0130pu\u00e7lar\u0131<\/h3>\n<p>Dosya y\u00fckleme API&#8217;lerinde g\u00fcvenlik her zaman \u00f6ncelikli olmal\u0131d\u0131r. A\u015fa\u011f\u0131daki ipu\u00e7lar\u0131, uygulaman\u0131z\u0131n g\u00fcvenli\u011fini art\u0131rman\u0131za yard\u0131mc\u0131 olacakt\u0131r:<\/p>\n<ol>\n<li><strong>Yetkilendirme ve Kimlik Do\u011frulama:<\/strong>\n<p>Her dosya y\u00fckleme iste\u011finin yetkili bir kullan\u0131c\u0131dan geldi\u011finden emin olun. JWT (JSON Web Tokens), OAuth veya API anahtarlar\u0131 gibi mekanizmalar kullanarak kullan\u0131c\u0131lar\u0131n kimli\u011fini do\u011frulay\u0131n ve dosya y\u00fckleme mutasyonuna eri\u015fimlerini yetkilendirin. \u00d6rne\u011fin, sadece oturum a\u00e7m\u0131\u015f kullan\u0131c\u0131lar\u0131n dosya y\u00fcklemesine izin verin.<\/p>\n<\/li>\n<li><strong>Dosya Tipi ve Boyutu Do\u011frulama:<\/strong>\n<p>Kullan\u0131c\u0131lar\u0131n y\u00fckleyebilece\u011fi dosya t\u00fcrlerini (MIME tipleri) ve maksimum dosya boyutunu s\u0131n\u0131rlay\u0131n. Bu, k\u00f6t\u00fc ama\u00e7l\u0131 dosyalar\u0131n (\u00f6rne\u011fin, \u00e7al\u0131\u015ft\u0131r\u0131labilir dosyalar) y\u00fcklenmesini \u00f6nler ve sunucu kaynaklar\u0131n\u0131n a\u015f\u0131r\u0131 kullan\u0131m\u0131n\u0131 engeller. Bu do\u011frulamay\u0131 hem istemci taraf\u0131nda (kullan\u0131c\u0131 deneyimi i\u00e7in) hem de sunucu taraf\u0131nda (g\u00fcvenlik i\u00e7in) yap\u0131n. Go&#8217;da <code>file.ContentType<\/code> ve <code>file.Size<\/code> alanlar\u0131n\u0131 kontrol edebilirsiniz.<\/p>\n<\/li>\n<li><strong>Benzersiz Dosya Adlar\u0131 ve Yollar:<\/strong>\n<p>Y\u00fcklenen dosyalar i\u00e7in benzersiz adlar ve g\u00fcvenli depolama yollar\u0131 kullan\u0131n (\u00f6rne\u011fin, <code>uuid.New().String()<\/code> ile). Bu, dizin ge\u00e7i\u015fi (directory traversal) sald\u0131r\u0131lar\u0131n\u0131 ve dosya adland\u0131rma \u00e7ak\u0131\u015fmalar\u0131n\u0131 \u00f6nler. Ayr\u0131ca, kullan\u0131c\u0131 taraf\u0131ndan sa\u011flanan dosya adlar\u0131n\u0131 do\u011frudan kullanmaktan ka\u00e7\u0131n\u0131n veya bunlar\u0131 g\u00fcvenli bir \u015fekilde temizleyin.<\/p>\n<\/li>\n<li><strong>Eri\u015fim Kontrol\u00fc (ACLs ve Bucket Politikalar\u0131):<\/strong>\n<p>Object Storage&#8217;daki dosyalar\u0131n\u0131z i\u00e7in uygun eri\u015fim kontrol listeleri (ACL&#8217;ler) veya bucket politikalar\u0131 belirleyin. Varsay\u0131lan olarak t\u00fcm dosyalar\u0131n herkese a\u00e7\u0131k olmas\u0131n\u0131 istemiyorsan\u0131z, <code>public-read<\/code> ACL&#8217;sini kullanmaktan ka\u00e7\u0131n\u0131n. Bunun yerine, sadece belirli kullan\u0131c\u0131lar\u0131n veya uygulaman\u0131z\u0131n dosyaya eri\u015fmesine izin veren daha k\u0131s\u0131tlay\u0131c\u0131 politikalar uygulay\u0131n.<\/p>\n<\/li>\n<li><strong>Vir\u00fcs Taramas\u0131 ve Zararl\u0131 \u0130\u00e7erik Kontrol\u00fc:<\/strong>\n<p>M\u00fcmk\u00fcnse, y\u00fcklenen dosyalar\u0131 vir\u00fcs ve zararl\u0131 yaz\u0131l\u0131m taramalar\u0131ndan ge\u00e7irin. Bu genellikle ayr\u0131 bir hizmet veya mikroservis arac\u0131l\u0131\u011f\u0131yla arka planda yap\u0131l\u0131r. Dosya taramadan ge\u00e7tikten sonra, g\u00fcvenli oldu\u011fu onaylan\u0131rsa eri\u015fime a\u00e7\u0131labilir.<\/p>\n<\/li>\n<\/ol>\n<p>Bu ileri d\u00fczey konular ve g\u00fcvenlik \u00f6nlemleri, Go GraphQL dosya y\u00fckleme API&#8217;nizin sadece i\u015flevsel de\u011fil, ayn\u0131 zamanda sa\u011flam, \u00f6l\u00e7eklenebilir ve g\u00fcvenli olmas\u0131n\u0131 sa\u011flayacakt\u0131r. \u00d6zellikle b\u00fcy\u00fck \u00f6l\u00e7ekli ve hassas veri i\u00e7eren uygulamalarda bu yakla\u015f\u0131mlar kritik \u00f6neme sahiptir.<\/p>\n<h2>Sonu\u00e7: Go ve GraphQL ile G\u00fc\u00e7l\u00fc Dosya Y\u00fckleme API&#8217;leri<\/h2>\n<p>Bu makale boyunca, Go programlama dilinin g\u00fcc\u00fcn\u00fc ve GraphQL&#8217;in esnekli\u011fini kullanarak S3 uyumlu Object Storage (DigitalOcean Spaces gibi) hizmetlerine dosya y\u00fckleyen sa\u011flam bir API&#8217;nin nas\u0131l olu\u015fturulaca\u011f\u0131n\u0131 ad\u0131m ad\u0131m inceledik. GraphQL&#8217;in tek bir endpoint \u00fczerinden esnek veri i\u015flemleri sunmas\u0131, Go&#8217;nun e\u015fzamanl\u0131l\u0131k ve performans yetenekleri ile birle\u015fti\u011finde, dosya y\u00fckleme gibi I\/O yo\u011fun g\u00f6revler i\u00e7in ideal bir \u00e7\u00f6z\u00fcm ortaya koydu. Ayr\u0131ca, Object Storage&#8217;\u0131n \u00f6l\u00e7eklenebilirlik, dayan\u0131kl\u0131l\u0131k ve maliyet etkinli\u011fi avantajlar\u0131n\u0131 da detayl\u0131ca ele ald\u0131k.<\/p>\n<p>Projenin ba\u015flang\u0131c\u0131ndan itibaren, Go mod\u00fcl\u00fcn\u00fc ba\u015flatma, <code>gqlgen<\/code> ile GraphQL \u015femas\u0131n\u0131 tan\u0131mlama, dosya y\u00fckleme mutasyonlar\u0131n\u0131 olu\u015fturma ve bu mutasyonlar i\u00e7in Go resolver fonksiyonlar\u0131n\u0131 geli\u015ftirme ad\u0131mlar\u0131n\u0131 takip ettik. \u00d6zellikle, AWS SDK for Go&#8217;yu kullanarak dosyalar\u0131 Spaces&#8217;a g\u00fcvenli ve verimli bir \u015fekilde nas\u0131l y\u00fckleyece\u011fimizi g\u00f6sterdik. \u0130leri d\u00fczey konular b\u00f6l\u00fcm\u00fcnde ise, b\u00fcy\u00fck dosya y\u00fcklemeleri i\u00e7in \u00f6n imzal\u0131 URL&#8217;ler gibi optimizasyon tekniklerine ve API&#8217;nizin g\u00fcvenli\u011fini sa\u011flamak i\u00e7in kimlik do\u011frulama, dosya do\u011frulama ve eri\u015fim kontrol\u00fc gibi kritik ipu\u00e7lar\u0131na de\u011findik. Bu bilgiler, uygulaman\u0131z\u0131n sadece mevcut ihtiya\u00e7lar\u0131n\u0131 kar\u015f\u0131lamakla kalmay\u0131p, ayn\u0131 zamanda gelecekteki b\u00fcy\u00fcme ve g\u00fcvenlik gereksinimlerine de haz\u0131r olmas\u0131n\u0131 sa\u011flayacakt\u0131r. Sonu\u00e7 olarak, Go ve GraphQL kombinasyonu, modern, y\u00fcksek performansl\u0131 ve s\u00fcrd\u00fcr\u00fclebilir dosya y\u00fckleme \u00e7\u00f6z\u00fcmleri geli\u015ftirmek i\u00e7in m\u00fckemmel bir ara\u00e7 setidir.<\/p>\n<h3>S\u0131k\u00e7a Sorulan Sorular<\/h3>\n<dl>\n<dt>GraphQL ile dosya y\u00fcklemek neden REST&#8217;ten daha iyi bir se\u00e7enek olabilir?<\/dt>\n<dd>GraphQL, dosya y\u00fckleme mutasyonunu di\u011fer t\u00fcm API i\u015flemlerinizle tek bir \u015fema alt\u0131nda birle\u015ftirmenize olanak tan\u0131r. Bu, API tutarl\u0131l\u0131\u011f\u0131n\u0131 art\u0131r\u0131r ve istemcilerin y\u00fcklenen dosyan\u0131n metadata&#8217;s\u0131n\u0131 (URL, boyut vb.) ayn\u0131 istekte almas\u0131n\u0131 sa\u011flar, bu da istemci taraf\u0131nda ek HTTP isteklerine olan ihtiyac\u0131 ortadan kald\u0131r\u0131r. Ayr\u0131ca, GraphQL&#8217;in esnekli\u011fi, API&#8217;nizin de\u011fi\u015fen ihtiya\u00e7lara daha kolay adapte olmas\u0131n\u0131 sa\u011flar.<\/dd>\n<dt>Hangi S3 uyumlu Object Storage servislerini kullanabilirim?<\/dt>\n<dd>Bu makalede DigitalOcean Spaces&#8217;\u0131 \u00f6rnek olarak kulland\u0131k, ancak Amazon S3, Google Cloud Storage (S3 uyumlu mod), Azure Blob Storage, Linode Object Storage, Wasabi ve MinIO (kendi sunucunuzda \u00e7al\u0131\u015ft\u0131r\u0131lan) gibi bir\u00e7ok S3 uyumlu Object Storage hizmetini kullanabilirsiniz. AWS SDK for Go, <code>Endpoint<\/code> yap\u0131land\u0131rmas\u0131 ile bu hizmetlerin \u00e7o\u011fuyla uyumlu \u00e7al\u0131\u015f\u0131r.<\/dd>\n<dt>B\u00fcy\u00fck dosyalar\u0131 y\u00fcklerken performans\u0131 nas\u0131l art\u0131rabilirim?<\/dt>\n<dd>B\u00fcy\u00fck dosyalar i\u00e7in en etkili y\u00f6ntem, &#8220;\u00f6n imzal\u0131 URL&#8217;ler&#8221; (Presigned URLs) kullanmakt\u0131r. Bu y\u00f6ntemle istemci, dosyay\u0131 do\u011frudan Object Storage&#8217;a y\u00fckler ve API sunucunuzun \u00fczerinden ge\u00e7mez, bu da sunucu y\u00fck\u00fcn\u00fc ve gecikmeyi azalt\u0131r. Ayr\u0131ca, \u00e7ok par\u00e7al\u0131 y\u00fckleme (multipart upload) ve istemci taraf\u0131nda dosya par\u00e7alama (chunking) teknikleri de b\u00fcy\u00fck dosya y\u00fcklemelerinin g\u00fcvenilirli\u011fini ve h\u0131z\u0131n\u0131 art\u0131rabilir.<\/dd>\n<dt>Y\u00fcklenen dosyalar\u0131n g\u00fcvenli\u011fini nas\u0131l sa\u011flayabilirim?<\/dt>\n<dd>G\u00fcvenlik i\u00e7in birden fazla katmanl\u0131 bir yakla\u015f\u0131m benimsemek \u00f6nemlidir:<\/p>\n<ol>\n<li>T\u00fcm dosya y\u00fckleme isteklerinde kullan\u0131c\u0131 kimlik do\u011frulamas\u0131 ve yetkilendirmesi yap\u0131n.<\/li>\n<li>Y\u00fcklenen dosya t\u00fcrlerini (MIME tipleri) ve boyutlar\u0131n\u0131 hem istemci hem de sunucu taraf\u0131nda do\u011frulay\u0131n.<\/li>\n<li>Dosya adlar\u0131 i\u00e7in benzersiz tan\u0131mlay\u0131c\u0131lar kullan\u0131n (UUID gibi) ve kullan\u0131c\u0131 taraf\u0131ndan sa\u011flanan adlar\u0131 do\u011frudan kullanmaktan ka\u00e7\u0131n\u0131n.<\/li>\n<li>Object Storage&#8217;da uygun eri\u015fim kontrol listeleri (ACL&#8217;ler) veya bucket politikalar\u0131 uygulay\u0131n.<\/li>\n<li>M\u00fcmk\u00fcnse, y\u00fcklenen dosyalar\u0131 vir\u00fcs ve k\u00f6t\u00fc ama\u00e7l\u0131 yaz\u0131l\u0131m taramas\u0131ndan ge\u00e7irin.<\/li>\n<\/ol>\n<\/dd>\n<dt><code>gqlgen<\/code> yerine ba\u015fka bir GraphQL k\u00fct\u00fcphanesi kullanabilir miyim?<\/dt>\n<dd>Evet, Go i\u00e7in <code>gqlgen<\/code> d\u0131\u015f\u0131nda <code>graphql-go\/graphql<\/code> gibi ba\u015fka GraphQL k\u00fct\u00fcphaneleri de mevcuttur. Ancak <code>gqlgen<\/code>, \u015fema odakl\u0131 yakla\u015f\u0131m\u0131 ve otomatik kod olu\u015fturma yetenekleri sayesinde genellikle daha h\u0131zl\u0131 geli\u015ftirme ve daha az boilerplate kodu sunar. Se\u00e7iminiz, projenizin \u00f6zel gereksinimlerine ve ekibinizin tercihlerine ba\u011fl\u0131 olabilir.<\/dd>\n<\/dl>\n<p><\/body><\/p>\n","protected":false},"excerpt":{"rendered":"Go ile GraphQL API: Dosya Y\u00fckleme \u0130\u015flemlerini Spaces&#8217;a Nas\u0131l Ta\u015f\u0131rs\u0131n\u0131z? Modern web uygulamalar\u0131, kullan\u0131c\u0131lar\u0131n dosya y\u00fcklemesine s\u0131k\u00e7a ihtiya\u00e7&hellip;","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"csco_page_header_type":"","csco_page_load_nextpost":"","csco_page_subscribe_form":"","csco_page_contact_form":"","footnotes":""},"categories":[1],"tags":[],"class_list":{"0":"post-41232","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-genel","7":"cs-entry","8":"cs-video-wrap"},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.5 (Yoast SEO v25.3.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>GraphQL Nedir ve Dosya Y\u00fcklemede Neden Tercih Edilmeli?<\/title>\n<meta name=\"description\" content=\"Modern web uygulamalar\u0131, kullan\u0131c\u0131lar\u0131n dosya y\u00fcklemesine s\u0131k\u00e7a ihtiya\u00e7 duyar.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/\" \/>\n<meta property=\"og:locale\" content=\"tr_TR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"GraphQL Nedir ve Dosya Y\u00fcklemede Neden Tercih Edilmeli?\" \/>\n<meta property=\"og:description\" content=\"Modern web uygulamalar\u0131, kullan\u0131c\u0131lar\u0131n dosya y\u00fcklemesine s\u0131k\u00e7a ihtiya\u00e7 duyar.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/\" \/>\n<meta property=\"og:site_name\" content=\"Kodlar\u0131n Gizemli D\u00fcnyas\u0131\" \/>\n<meta property=\"article:published_time\" content=\"2026-03-13T11:02:23+00:00\" \/>\n<meta name=\"author\" content=\"Fatih Soysal\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Yazan:\" \/>\n\t<meta name=\"twitter:data1\" content=\"Fatih Soysal\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tahmini okuma s\u00fcresi\" \/>\n\t<meta name=\"twitter:data2\" content=\"30 dakika\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/\"},\"author\":{\"name\":\"Fatih Soysal\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"headline\":\"GraphQL Nedir ve Dosya Y\u00fcklemede Neden Tercih Edilmeli?\",\"datePublished\":\"2026-03-13T11:02:23+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/\"},\"wordCount\":4773,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/#respond\"]}],\"copyrightYear\":\"2026\",\"copyrightHolder\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/\",\"name\":\"GraphQL Nedir ve Dosya Y\u00fcklemede Neden Tercih Edilmeli?\",\"isPartOf\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#website\"},\"datePublished\":\"2026-03-13T11:02:23+00:00\",\"description\":\"Modern web uygulamalar\u0131, kullan\u0131c\u0131lar\u0131n dosya y\u00fcklemesine s\u0131k\u00e7a ihtiya\u00e7 duyar.\",\"breadcrumb\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/#breadcrumb\"},\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Anasayfa\",\"item\":\"https:\/\/fatihsoysal.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"GraphQL Nedir ve Dosya Y\u00fcklemede Neden Tercih Edilmeli?\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#website\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/\",\"name\":\"Fatihsoysal.com\",\"description\":\"Blog - Yaz\u0131l\u0131m D\u00fcnyas\u0131 Tecr\u00fcbelerim\",\"publisher\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/fatihsoysal.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"tr\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1\",\"name\":\"Fatih Soysal\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"tr\",\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png\",\"contentUrl\":\"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png\",\"width\":512,\"height\":512,\"caption\":\"Fatih Soysal\"},\"logo\":{\"@id\":\"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/\"},\"description\":\"Kullan\u0131m ve kodlama m\u00fckemmeliyetini odak alan uygulamalar olu\u015fturma deneyimine sahip, profesyonel olarak 15+ y\u0131l \u00fczeri deneyime sahip bir yaz\u0131l\u0131m m\u00fchendisi.\",\"url\":\"https:\/\/fatihsoysal.com\/blog\/author\/fatihsoysal\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"GraphQL Nedir ve Dosya Y\u00fcklemede Neden Tercih Edilmeli?","description":"Modern web uygulamalar\u0131, kullan\u0131c\u0131lar\u0131n dosya y\u00fcklemesine s\u0131k\u00e7a ihtiya\u00e7 duyar.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/","og_locale":"tr_TR","og_type":"article","og_title":"GraphQL Nedir ve Dosya Y\u00fcklemede Neden Tercih Edilmeli?","og_description":"Modern web uygulamalar\u0131, kullan\u0131c\u0131lar\u0131n dosya y\u00fcklemesine s\u0131k\u00e7a ihtiya\u00e7 duyar.","og_url":"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/","og_site_name":"Kodlar\u0131n Gizemli D\u00fcnyas\u0131","article_published_time":"2026-03-13T11:02:23+00:00","author":"Fatih Soysal","twitter_card":"summary_large_image","twitter_misc":{"Yazan:":"Fatih Soysal","Tahmini okuma s\u00fcresi":"30 dakika"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/#article","isPartOf":{"@id":"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/"},"author":{"name":"Fatih Soysal","@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"headline":"GraphQL Nedir ve Dosya Y\u00fcklemede Neden Tercih Edilmeli?","datePublished":"2026-03-13T11:02:23+00:00","mainEntityOfPage":{"@id":"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/"},"wordCount":4773,"commentCount":0,"publisher":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"inLanguage":"tr","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/#respond"]}],"copyrightYear":"2026","copyrightHolder":{"@id":"https:\/\/fatihsoysal.com\/blog\/#organization"}},{"@type":"WebPage","@id":"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/","url":"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/","name":"GraphQL Nedir ve Dosya Y\u00fcklemede Neden Tercih Edilmeli?","isPartOf":{"@id":"https:\/\/fatihsoysal.com\/blog\/#website"},"datePublished":"2026-03-13T11:02:23+00:00","description":"Modern web uygulamalar\u0131, kullan\u0131c\u0131lar\u0131n dosya y\u00fcklemesine s\u0131k\u00e7a ihtiya\u00e7 duyar.","breadcrumb":{"@id":"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/#breadcrumb"},"inLanguage":"tr","potentialAction":[{"@type":"ReadAction","target":["https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/fatihsoysal.com\/blog\/graphql-nedir-ve-dosya-yuklemede-neden-tercih-edilmeli\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Anasayfa","item":"https:\/\/fatihsoysal.com\/blog\/"},{"@type":"ListItem","position":2,"name":"GraphQL Nedir ve Dosya Y\u00fcklemede Neden Tercih Edilmeli?"}]},{"@type":"WebSite","@id":"https:\/\/fatihsoysal.com\/blog\/#website","url":"https:\/\/fatihsoysal.com\/blog\/","name":"Fatihsoysal.com","description":"Blog - Yaz\u0131l\u0131m D\u00fcnyas\u0131 Tecr\u00fcbelerim","publisher":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/fatihsoysal.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"tr"},{"@type":["Person","Organization"],"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/002a254750921dcfd568a99e48240dd1","name":"Fatih Soysal","image":{"@type":"ImageObject","inLanguage":"tr","@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png","contentUrl":"https:\/\/fatihsoysal.com\/blog\/wp-content\/uploads\/2024\/04\/cropped-replicate-prediction-3kgg1hgjn5rgp0cf0p5tr0jw7w-1.png","width":512,"height":512,"caption":"Fatih Soysal"},"logo":{"@id":"https:\/\/fatihsoysal.com\/blog\/#\/schema\/person\/image\/"},"description":"Kullan\u0131m ve kodlama m\u00fckemmeliyetini odak alan uygulamalar olu\u015fturma deneyimine sahip, profesyonel olarak 15+ y\u0131l \u00fczeri deneyime sahip bir yaz\u0131l\u0131m m\u00fchendisi.","url":"https:\/\/fatihsoysal.com\/blog\/author\/fatihsoysal\/"}]}},"yoast_meta":{"yoast_wpseo_title":"","yoast_wpseo_metadesc":"","yoast_wpseo_canonical":""},"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts\/41232","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/comments?post=41232"}],"version-history":[{"count":0,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/posts\/41232\/revisions"}],"wp:attachment":[{"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/media?parent=41232"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/categories?post=41232"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fatihsoysal.com\/blog\/wp-json\/wp\/v2\/tags?post=41232"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}