Yıllar süren hizmet geliştirme sürecim boyunca, RESTful API benim ilk tercihim oldu. Ancak REST'in avantajları olmasına rağmen bu, her kullanım durumu için en iyi yaklaşım olduğu anlamına gelmez. Yıllar geçtikçe, bazen belirli senaryolar için daha iyi alternatiflerin bulunabileceğini öğrendim. Sırf bu konuda tutkulu olduğum için REST'e bağlı kalmak (doğru uyum olmadığında) yalnızca teknik borçla ve ürün sahibiyle gergin bir ilişkiyle sonuçlanır.
RESTful yaklaşımının en büyük sıkıntılı noktalarından biri, bir iş kararı için gerekli tüm bilgileri almak için birden fazla talepte bulunma ihtiyacıdır.
Örnek olarak, bir müşterinin 360 derecelik görüntüsünü istediğimi varsayalım. Aşağıdaki istekleri yapmam gerekecek:
GET /customers/{some_token}
temel müşteri bilgilerini sağlarGET /addresses/{some_token}
gerekli adresi sağlarGET /contacts/{some_token}
iletişim bilgilerini döndürürGET /credit/{some_token}
önemli finansal bilgileri döndürür
REST'in temel amacının, yanıtları her kaynak için lazer odaklı tutmak olduğunu anlasam da, bu senaryo tüketici tarafında daha fazla çalışma yapılmasını sağlıyor. Bir kuruluşun müşteriyle gelecekteki işleriyle ilgili kararlar almasına yardımcı olacak bir kullanıcı arayüzü oluşturmak için tüketicinin birden fazla arama yapması gerekir.
Bu makalede, GraphQL'in neden bir RESTful API'ye göre tercih edilen yaklaşım olduğunu göstereceğim ve GraphQL ile hızlı bir şekilde çalışmaya başlamak için Apollo Sunucusunun (ve Apollo Explorer'ın) nasıl dağıtılacağını göstereceğim.
Çözümümü Node.js ile oluşturmayı ve çözümümü Heroku'ya dağıtmayı planlıyorum.
GraphQL'in REST'ten daha iyi bir yaklaşım olduğu birkaç yaygın kullanım durumu vardır:
GraphQL ile ilgili becerilerim pek gelişmiş olmadığından bu makale için Apollo Server'ı kullanmaya karar verdim.
Apollo Server, herhangi bir GraphQL şemasıyla çalışan bir GraphQL sunucusudur. Amaç, GraphQL API oluşturma sürecini basitleştirmektir. Temel tasarım, Express veya Koa gibi çerçevelerle iyi bir şekilde bütünleşir. Bir sonraki makalemde gerçek zamanlı veriler için aboneliklerden ( graphql-ws kütüphanesi aracılığıyla) yararlanma yeteneğini keşfedeceğim.
Apollo Server'ın gerçekten parladığı yer, geliştiricilerin GraphQL API'lerini keşfetmek ve test etmek için kullanabilecekleri yerleşik bir web arayüzü olan Apollo Explorer'dır. Sorguların kolay oluşturulmasına ve API şemasının grafiksel formatta görüntülenmesine olanak sağladığı için stüdyo benim için mükemmel bir seçim olacaktır.
Bu örnekte, müşterinin 360 derecelik görünümünü sağlamak için aşağıdaki şemaya ihtiyacımız olduğunu varsayalım:
type Customer { token: String name: String sic_code: String } type Address { token: String customer_token: String address_line1: String address_line2: String city: String state: String postal_code: String } type Contact { token: String customer_token: String first_name: String last_name: String email: String phone: String } type Credit { token: String customer_token: String credit_limit: Float balance: Float credit_score: Int }
Aşağıdaki GraphQL sorgularına odaklanmayı planlıyorum:
type Query { addresses: [Address] address(customer_token: String): Address contacts: [Contact] contact(customer_token: String): Contact customers: [Customer] customer(token: String): Customer credits: [Credit] credit(customer_token: String): Credit }
Tüketiciler, görüntülemek istedikleri Müşteriye jetonu sağlayacaktır. Ayrıca uygun Adres, Kişi ve Kredi nesnelerini de almayı bekliyoruz. Amaç, tüm bu bilgiler için tek bir API çağrısı yerine dört farklı API çağrısı yapmaktan kaçınmaktır.
Yerel iş istasyonumda graphql-server-customer
adında yeni bir klasör oluşturarak başladım. Daha sonra Apollo Sunucu belgelerinin Başlarken bölümünü kullanarak TypeScript yaklaşımını kullanarak birinci ve ikinci adımları izledim.
Daha sonra şemamı tanımladım ve test için bazı statik verileri de ekledim. Normalde bir veritabanına bağlanırdık, ancak statik veriler bu demoda iyi çalışacaktır.
Güncellenmiş index.ts
dosyam aşağıdadır:
import { ApolloServer } from '@apollo/server'; import { startStandaloneServer } from '@apollo/server/standalone'; const typeDefs = `#graphql type Customer { token: String name: String sic_code: String } type Address { token: String customer_token: String address_line1: String address_line2: String city: String state: String postal_code: String } type Contact { token: String customer_token: String first_name: String last_name: String email: String phone: String } type Credit { token: String customer_token: String credit_limit: Float balance: Float credit_score: Int } type Query { addresses: [Address] address(customer_token: String): Address contacts: [Contact] contact(customer_token: String): Contact customers: [Customer] customer(token: String): Customer credits: [Credit] credit(customer_token: String): Credit } `; const resolvers = { Query: { addresses: () => addresses, address: (parent, args, context) => { const customer_token = args.customer_token; return addresses.find(address => address.customer_token === customer_token); }, contacts: () => contacts, contact: (parent, args, context) => { const customer_token = args.customer_token; return contacts.find(contact => contact.customer_token === customer_token); }, customers: () => customers, customer: (parent, args, context) => { const token = args.token; return customers.find(customer => customer.token === token); }, credits: () => credits, credit: (parent, args, context) => { const customer_token = args.customer_token; return credits.find(credit => credit.customer_token === customer_token); } }, }; const server = new ApolloServer({ typeDefs, resolvers, }); const { url } = await startStandaloneServer(server, { listen: { port: 4000 }, }); console.log(`Apollo Server ready at: ${url}`); const customers = [ { token: 'customer-token-1', name: 'Acme Inc.', sic_code: '1234' }, { token: 'customer-token-2', name: 'Widget Co.', sic_code: '5678' } ]; const addresses = [ { token: 'address-token-1', customer_token: 'customer-token-1', address_line1: '123 Main St.', address_line2: '', city: 'Anytown', state: 'CA', postal_code: '12345' }, { token: 'address-token-22', customer_token: 'customer-token-2', address_line1: '456 Elm St.', address_line2: '', city: 'Othertown', state: 'NY', postal_code: '67890' } ]; const contacts = [ { token: 'contact-token-1', customer_token: 'customer-token-1', first_name: 'John', last_name: 'Doe', email: '[email protected]', phone: '123-456-7890' } ]; const credits = [ { token: 'credit-token-1', customer_token: 'customer-token-1', credit_limit: 10000.00, balance: 2500.00, credit_score: 750 } ];
Her şey beklendiği gibi yapılandırıldığında, sunucuyu başlatmak için aşağıdaki komutu çalıştırıyoruz:
$ npm start
Apollo sunucusu 4000 numaralı bağlantı noktasında çalışırken, Apollo Explorer'a erişmek için http://localhost:4000/ URL'sini kullandım. Daha sonra aşağıdaki örnek sorguyu oluşturdum:
query ExampleQuery { addresses { token } contacts { token } customers { token } }
Apollo Explorer'da şöyle görünüyor:
Örnek Sorgu düğmesine basarak yanıt yükünün index.ts
dosyasında sağladığım statik verilerle uyumlu olduğunu doğruladım:
{ "data": { "addresses": [ { "token": "address-token-1" }, { "token": "address-token-22" } ], "contacts": [ { "token": "contact-token-1" } ], "customers": [ { "token": "customer-token-1" }, { "token": "customer-token-2" } ] } }
Müşteri 360 kullanım durumumu ele alma konusunda daha ileri gitmeden önce bu hizmeti bulutta çalıştırmak istedim.
Bu makale tamamen yeni bir şey yapmakla ilgili olduğundan Apollo sunucumu Heroku'ya dağıtmanın ne kadar zor olacağını görmek istedim.
Yerel olarak çalıştırma ile bulutta bir yerde çalıştırma arasındaki bağlantı noktası numarası farklılıklarını çözmem gerektiğini biliyordum. Sunucuyu başlatmak için kodumu aşağıda gösterildiği gibi güncelledim:
const { url } = await startStandaloneServer(server, { listen: { port: Number.parseInt(process.env.PORT) || 4000 }, });
Bu güncellemeyle birlikte, bir ortam değişkeninde PORT değeri belirtilmediği sürece 4000 numaralı bağlantı noktasını kullanacağız.
Gitlab'ı kullanarak bu dosyalar için yeni bir proje oluşturdum ve Heroku komut satırı arayüzünü (CLI) kullanarak Heroku hesabımda oturum açtım:
$ heroku login
Heroku'da CLI veya Heroku kontrol paneli web kullanıcı arayüzüyle yeni bir uygulama oluşturabilirsiniz. Bu makale için CLI'yi kullanacağız:
$ heroku create jvc-graphql-server-customer
CLI komutu aşağıdaki yanıtı döndürdü:
Creating ⬢ jvc-graphql-server-customer... done https://jvc-graphql-server-customer-b62b17a2c949.herokuapp.com/ | https://git.heroku.com/jvc-graphql-server-customer.git
Komut ayrıca Heroku tarafından uzaktan kumanda olarak kullanılan depoyu da otomatik olarak ekledi:
$ git remote heroku origin
Apollo Server, üretim ortamlarında Apollo Explorer'ı varsayılan olarak devre dışı bırakır. Demom için onu Heroku'da çalışır durumda bırakmak istiyorum. Bunu yapmak için NODE_ENV
ortam değişkenini geliştirmeye ayarlamam gerekiyor. Bunu aşağıdaki CLI komutuyla ayarlayabilirim:
$ heroku config:set NODE_ENV=development
CLI komutu aşağıdaki yanıtı döndürdü:
Setting NODE_ENV and restarting ⬢ jvc-graphql-server-customer... done, v3 NODE_ENV: development
Artık kodumuzu Heroku'ya dağıtabilecek konumdayız:
$ git commit --allow-empty -m 'Deploy to Heroku' $ git push heroku
Heroku Kontrol Paneline hızlı bir bakış, Apollo Sunucumun sorunsuz çalıştığını gösteriyor:
Heroku'da yeniyseniz bu kılavuz size nasıl yeni bir hesap oluşturacağınızı ve Heroku CLI'yi nasıl yükleyeceğinizi gösterecektir.
GraphQL ile Müşteri 360 kullanım durumum için kabul kriterlerini aşağıdaki sorguyla karşılayabilirim:
query CustomerData($token: String) { customer(token: $token) { name sic_code token }, address(customer_token: $token) { token customer_token address_line1 address_line2 city state postal_code }, contact(customer_token: $token) { token, customer_token, first_name, last_name, email, phone }, credit(customer_token: $token) { token, customer_token, credit_limit, balance, credit_score } }
Tek yapmam gereken customer-token-1
değerine sahip tek bir Customer token
değişkenini iletmek:
{ "token": "customer-token-1" }
Tek bir GraphQL API çağrısı kullanarak tüm verilere ulaşabiliriz:
{ "data": { "customer": { "name": "Acme Inc.", "sic_code": "1234", "token": "customer-token-1" }, "address": { "token": "address-token-1", "customer_token": "customer-token-1", "address_line1": "123 Main St.", "address_line2": "", "city": "Anytown", "state": "CA", "postal_code": "12345" }, "contact": { "token": "contact-token-1", "customer_token": "customer-token-1", "first_name": "John", "last_name": "Doe", "email": "[email protected]", "phone": "123-456-7890" }, "credit": { "token": "credit-token-1", "customer_token": "customer-token-1", "credit_limit": 10000, "balance": 2500, "credit_score": 750 } } }
Aşağıda Heroku uygulamamdan çalıştırılan Apollo Explorer'ın ekran görüntüsü var:
Kariyerimin başlarında Java ve C#'ın geliştiricilerin benimsenmesi için birbirleriyle rekabet ettiği zamanları hatırlıyorum. Tartışmanın her iki tarafındaki savunucular, seçtikleri teknolojinin en iyi seçim olduğunu kanıtlamaya hazırdı… öyle olmasa bile.
Bu örnekte Müşteri 360 kullanım senaryomu birden fazla şekilde karşılayabilirdik. Kanıtlanmış bir RESTful API kullanmak işe yarayabilirdi ancak gerekli tüm verileri almak için birden fazla API çağrısı yapılması gerekirdi. Apollo Server ve GraphQL'i kullanmak, tek bir API çağrısıyla hedeflerime ulaşmamı sağladı.
Ayrıca terminalimdeki birkaç komutla GraphQL sunucumu Heroku'ya dağıtmanın ne kadar kolay olduğunu da seviyorum. Bu, altyapının yükünü hafifleterek ve kodumu güvenilir bir üçüncü taraf sağlayıcıda çalıştırarak uygulamaya odaklanmamı sağlıyor. En önemlisi, bu benim kişisel misyon beyanımla tam olarak örtüşüyor:
“Zamanınızı fikri mülkiyetinizin değerini artıran özellikler/işlevsellik sunmaya odaklayın. Diğer her şey için çerçevelerden, ürünlerden ve hizmetlerden yararlanın."
– J. Vester
Bu makalenin kaynak koduyla ilgileniyorsanız GitLab'da mevcuttur.
Ama durun… dahası da var!
Takip eden yazımda, aboneliklerle kimlik doğrulama ve gerçek zamanlı veri alımını uygulamak için GraphQL sunucumuzu daha da geliştireceğiz.
Gerçekten harika bir gün geçirin!