Mano GraphQL įgūdžių išlyginimas: prenumeratos realiuoju laiku

Jau kelerius metus bandžiau nustatyti sistemas, produktus ir paslaugas, kurios leistų technologams toliau sutelkti dėmesį į savo intelektinės nuosavybės vertės didinimą. Tai man ir toliau yra nuostabi kelionė, kupina unikalių mokymosi galimybių.
Mano inžinierius neseniai susimąstė, ar yra situacija, kai galėčiau rasti antrinės naudos esamai koncepcijai, apie kurią jau kalbėjau anksčiau. Kitaip tariant, ar galėčiau nustatyti kitą naudą, kurios poveikis toks pat, kaip ir anksčiau pripažintas pirminis sprendimas?
Šiam straipsniui norėjau pasinerti į GraphQL, kad pamatyčiau, ką galėčiau rasti.
Straipsnyje „Kai laikas pailsėti“ kalbėjau apie tai, kaip egzistuoja realūs scenarijai, kai „GraphQL“ yra geriau nei „RESTful“ paslauga. Apžvelgėme, kaip sukurti ir įdiegti GraphQL API naudojant „Apollo Server“.
Šiame tolesniame įraše planuoju patobulinti savo žinias apie GraphQL, eidamas per prenumeratas, skirtas duomenų gavimui realiuoju laiku. Taip pat sukursime „WebSocket“ paslaugą, kad sunaudotume prenumeratas.
Santrauka: Kliento 360 naudojimo atvejis
Mano ankstesnis straipsnis buvo susijęs su „Customer 360“ naudojimo atvejis, kai mano išgalvotos įmonės globėjai saugo šiuos duomenų rinkinius:
- Kliento informacija
- Adreso informacija
- Susisiekimo būdai
- Kredito atributai
Didžiulis laimėjimas naudojant GraphQL yra tai, kad viena GraphQL užklausa gali gauti visus reikiamus duomenis kliento prieigos raktui (unikali tapatybė).
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
}
Naudojant RESTful metodą norint gauti vieną (360) kliento vaizdą, būtų reikėję sujungti keletą užklausų ir atsakymų. GraphQL suteikia mums sprendimą, kuris veikia daug geriau.
Tikslai aukštyn lygiu
Norint pasiekti aukštesnį lygį bet kurioje gyvenimo srityje, reikia siekti naujų tikslų. Mano tikslams čia tai reiškia:
- „GraphQL“ prenumeratos vertės pasiūlymo supratimas ir įgyvendinimas.
- „WebSocket“ diegimo naudojimas norint sunaudoti „GraphQL“ prenumeratą.
Idėja naudoti prenumeratas per GraphQL užklausas ir mutacijas yra tinkamiausias metodas, kai tenkinamos šios sąlygos:
- Maži, laipsniški didelių objektų pakeitimai
- Mažos delsos atnaujinimai realiuoju laiku (pvz., pokalbių programa)
Tai svarbu, nes prenumeratos įdiegimas GraphQL nėra trivialus. Reikės atnaujinti ne tik pagrindinį serverį, bet ir daug naudojančią programą.
Laimei, naudojimo atvejis, kurio ieškome su „Customer 360“ pavyzdžiu, puikiai tinka prenumeratai. Be to, mes įgyvendinsime WebSocket metodą, kad panaudotume šias prenumeratas.
Kaip ir anksčiau, toliau naudosiu „Apollo“.
Išlyginimas su prenumeratos kreditais
Pirmiausia turime įdiegti reikiamas bibliotekas, kad palaikytume prenumeratas su mano Apollo GraphQL serveriu:
npm install ws
npm install graphql-ws @graphql-tools/schema
npm install graphql-subscriptions
Įdiegęs tuos elementus sutelkiau dėmesį į atnaujinimą index.ts
iš mano pradinės saugyklos, kad pratęsčiau typedefs
pastovus su šiais dalykais:
type Subscription {
creditUpdated: Credit
}
Taip pat įkūriau pastovią, kad įsikurčiau naują PubSub
pavyzdį ir sukūrėme prenumeratos pavyzdį, kurį naudosime vėliau:
const pubsub = new PubSub();
pubsub.publish('CREDIT_BALANCE_UPDATED', {
creditUpdated: {
}
});
Išvaliau esamus sprendimus ir pridėjau naują Subscription
šiam naujam naudojimo atvejui:
const resolvers = {
Query: {
addresses: () => addresses,
address: (parent, args) => {
const customer_token = args.customer_token;
return addresses.find(address => address.customer_token === customer_token);
},
contacts: () => contacts,
contact: (parent, args) => {
const customer_token = args.customer_token;
return contacts.find(contact => contact.customer_token === customer_token);
},
customers: () => customers,
customer: (parent, args) => {
const token = args.token;
return customers.find(customer => customer.token === token);
},
credits: () => credits,
credit: (parent, args) => {
const customer_token = args.customer_token;
return credits.find(credit => credit.customer_token === customer_token);
}
},
Subscription: {
creditUpdated: {
subscribe: () => pubsub.asyncIterator(('CREDIT_BALANCE_UPDATED')),
}
}
};
Tada pakeičiau serverio konfigūraciją ir pristatau prenumeratos dizainą:
const app = express();
const httpServer = createServer(app);
const wsServer = new WebSocketServer({
server: httpServer,
path: '/graphql'
});
const schema = makeExecutableSchema({ typeDefs, resolvers });
const serverCleanup = useServer({ schema }, wsServer);
const server = new ApolloServer({
schema,
plugins: (
ApolloServerPluginDrainHttpServer({ httpServer }),
{
async serverWillStart() {
return {
async drainServer() {
serverCleanup.dispose();
}
};
}
}
),
});
await server.start();
app.use('/graphql', cors(), express.json(), expressMiddleware(server, {
context: async () => ({ pubsub })
}));
const PORT = Number.parseInt(process.env.PORT) || 4000;
httpServer.listen(PORT, () => {
console.log(`Server is now running on http://localhost:${PORT}/graphql`);
console.log(`Subscription is now running on ws://localhost:${PORT}/graphql`);
});
Siekdamas imituoti klientų pagrįstus atnaujinimus, sukūriau šį metodą, kad padidinčiau kredito likutį 50 USD kas penkias sekundes, kol paslauga veikia. Kai likutis pasiekia (arba viršija) 10 000 USD kredito limitą, iš naujo nustatau likutį į 2 500 USD, imituodamas atliekamą balanso mokėjimą.
function incrementCreditBalance() {
if (credits(0).balance >= credits(0).credit_limit) {
credits(0).balance = 0.00;
console.log(`Credit balance reset to ${credits(0).balance}`);
} else {
credits(0).balance += 50.00;
console.log(`Credit balance updated to ${credits(0).balance}`);
}
pubsub.publish('CREDIT_BALANCE_UPDATED', { creditUpdated: credits(0) });
setTimeout(incrementCreditBalance, 5000);
}
incrementCreditBalance();
Pilnas index.ts
failą galite rasti čia.
Dislokuoti į Heroku
Kai paslauga yra paruošta, laikas įdiegti paslaugą, kad galėtume su ja bendrauti. Kadangi „Heroku“ praeitą kartą pasiteisino puikiai (ir man juo lengva naudotis), laikykimės šio požiūrio.
Norėdami pradėti, turėjau paleisti šias Heroku CLI komandas:
$ heroku login
$ heroku create jvc-graphql-server-sub
Creating ⬢ jvc-graphql-server-sub... done
https://jvc-graphql-server-sub-1ec2e6406a82.herokuapp.com/ | https://git.heroku.com/jvc-graphql-server-sub.git
Komanda taip pat automatiškai įtraukė saugyklą, kurią Heroku naudojo kaip nuotolinį:
$ git remote
heroku
origin
Kaip jau minėjau savo ankstesniame straipsnyje, „Apollo Server“ išjungia „Apollo Explorer“ gamybos aplinkoje. Kad „Apollo Explorer“ būtų prieinama mūsų poreikiams, turėjau nustatyti NODE_ENV
vystymuisi kintama aplinka. Aš tai nustatiau su šia CLI komanda:
$ heroku config:set NODE_ENV=development
Setting NODE_ENV and restarting ⬢ jvc-graphql-server-sub... done, v3
NODE_ENV: development
Buvau pasirengęs įdiegti savo kodą Heroku:
$ git commit --allow-empty -m 'Deploy to Heroku'
$ git push heroku
Greitas „Heroku“ prietaisų skydelio vaizdas parodė, kad mano „Apollo“ serveris veikia be jokių problemų:
Skiltyje „Nustatymai“ radau šios paslaugos egzemplioriaus „Heroku“ programos URL:
https://jvc-graphql-server-sub-1ec2e6406a82.herokuapp.com/
Atkreipkite dėmesį – paskelbus šį straipsnį ši nuoroda nebebus naudojama.
Kol kas prie šio URL galėčiau pridėti GraphQL, kad paleisčiau „Apollo Server Studio“. Tai leis man pamatyti, kaip prenumeratos veikia taip, kaip tikėtasi:
Atkreipkite dėmesį į prenumeratos atsakymus dešinėje ekrano pusėje.
Lygių padidinimas naudojant „WebSocket Skillz“.
Galime panaudoti „WebSocket“ palaikymą ir „Heroku“ galimybes, kad sukurtume diegimą, kuris sunaudoja mūsų sukurtą prenumeratą.
Mano atveju sukūriau failą index.js su tokiu turiniu. Iš esmės tai sukūrė „WebSocket“ klientą ir taip pat sukūrė netikrą HTTP paslaugą, kurią galėjau naudoti norėdamas patikrinti, ar klientas veikia:
import { createClient } from "graphql-ws";
import { WebSocket } from "ws";
import http from "http";
// Create a dummy HTTP server to bind to Heroku's $PORT
const PORT = process.env.PORT || 3000;
http.createServer((req, res) => res.end('Server is running')).listen(PORT, () => {
console.log(`HTTP server running on port ${PORT}`);
});
const host_url = process.env.GRAPHQL_SUBSCRIPTION_HOST || 'ws://localhost:4000/graphql';
const client = createClient({
url: host_url,
webSocketImpl: WebSocket
});
const query = `subscription {
creditUpdated {
token
customer_token
credit_limit
balance
credit_score
}
}`;
function handleCreditUpdated(data) {
console.log('Received credit update:', data);
}
// Subscribe to the creditUpdated subscription
client.subscribe(
{
query,
},
{
next: (data) => handleCreditUpdated(data.data.creditUpdated),
error: (err) => console.error('Subscription error:', err),
complete: () => console.log('Subscription complete'),
}
);
Pilnas index.js
failą galite rasti čia.
Šią paprastą „Node.js“ programą galime įdiegti ir „Heroku“, būtinai nustatydami GRAPHQL_SUBSCRIPTION_HOST
aplinkos kintamąjį į Heroku programos URL, kurį naudojome anksčiau.
Taip pat sukūriau štai ką Procfile
pasakyti Heroku, kaip paleisti programą:
web: node src/index.js
Tada sukūriau naują Heroku programą:
$ heroku create jvc-websocket-example
Creating ⬢ jvc-websocket-example... done
https://jvc-websocket-example-62824c0b1df4.herokuapp.com/ | https://git.heroku.com/jvc-websocket-example.git
Tada aš nustatiau GRAPHQL_SUBSCRIPTION_HOST
aplinkos kintamasis, nurodantis į mano veikiantį GraphQL serverį:
$ heroku --app jvc-websocket-example \
config:set \
GRAPHQL_SUBSCRIPTION_HOST=ws://jvc-graphql-server-sub-1ec2e6406a82.herokuapp.com/graphql
Šiuo metu esame pasirengę įdiegti savo kodą Heroku:
$ git commit --allow-empty -m 'Deploy to Heroku'
$ git push heroku
Kai WebSocket klientas paleidžiamas, jo būseną galime pamatyti Heroku prietaisų skydelyje:
Peržiūrėdami žurnalus „Heroku“ prietaisų skydelyje jvc-websocket-example
Pavyzdžiui, galime matyti kelis atnaujinimus balance
nuosavybė jvc-graphql-server-sub
paslauga. Savo demonstracijoje netgi galėjau užfiksuoti naudojimo atvejį, kai likutis buvo sumažintas iki nulio, imituojant, kad buvo atliktas mokėjimas:
Terminale tuos pačius žurnalus galime pasiekti naudodami CLI komandą heroku žurnalai.
2024-08-28T12:14:48.463846+00:00 app(web.1): Received credit update: {
2024-08-28T12:14:48.463874+00:00 app(web.1): token: 'credit-token-1',
2024-08-28T12:14:48.463875+00:00 app(web.1): customer_token: 'customer-token-1',
2024-08-28T12:14:48.463875+00:00 app(web.1): credit_limit: 10000,
2024-08-28T12:14:48.463875+00:00 app(web.1): balance: 9950,
2024-08-28T12:14:48.463876+00:00 app(web.1): credit_score: 750
2024-08-28T12:14:48.463876+00:00 app(web.1): }
Mes ne tik turime GraphQL paslaugą su prenumeratos diegimu, bet dabar turime WebSocket klientą, kuris naudoja tuos naujinimus.
Išvada
Mano skaitytojai gali prisiminti mano asmeninę misiją, kuri, manau, gali būti taikoma bet kuriam IT specialistui:
„Sutelkite savo laiką į funkcijų / funkcijų, kurios padidina jūsų intelektinės nuosavybės vertę, pristatymą. Visam kitam pritaikykite sistemas, produktus ir paslaugas. – J. Vesteris
Šiame gilumoje į GraphQL prenumeratas sėkmingai panaudojome naujinius iš „Apollo“ serverio, veikiančio „Heroku“, naudodami kitą paslaugą, taip pat veikiančią „Heroku“ – „Node.js“ pagrindu veikiančią programą, kuri naudoja „WebSockets“. Naudodami lengvas prenumeratas išvengėme užklausų dėl nesikeičiančių duomenų siuntimo, o tiesiog užsiprenumeravome, kad gautume kredito likučio atnaujinimus, kai jie įvyktų.
Įžangoje minėjau ieškoti papildomos vertės principo temoje, apie kurią rašiau anksčiau. „GraphQL“ prenumeratos yra puikus pavyzdys to, ką turėjau omenyje, nes tai leidžia vartotojams nedelsiant gauti naujinimus, nereikalaujant šaltinio duomenų užklausų. Tai labai sujaudins „Customer 360“ duomenų vartotojus, žinodami, kad jie galės gauti naujinius, kai jie įvyks.
Heroku yra dar vienas pavyzdys, kuris ir toliau laikosi mano misijos, siūlydamas platformą, leidžiančią greitai sukurti sprendimų prototipus naudojant CLI ir standartines Git komandas. Tai ne tik suteikia man paprastą būdą parodyti savo prenumeratos naudojimo atvejį, bet ir pritaikyti vartotoją naudojant WebSockets.
Jei jus domina šio straipsnio šaltinio kodas, peržiūrėkite mano „GitLab“ saugyklas:
Jaučiuosi užtikrintai, kai sakau, kad šiomis pastangomis sėkmingai ištobulinau savo GraphQL įgūdžius. Ši kelionė man buvo nauja ir sudėtinga… be to, labai smagi!
Toliau ketinu pasinerti į autentifikavimą, kuris, tikiuosi, suteiks dar vieną galimybę pasiekti aukštesnį lygį naudojant „GraphQL“ ir „Apollo Server“. Sekite naujienas!
Tikrai puikios dienos!