Voor enkele jaren heb ik geprobeerd te identificeren welke frameworks, producten en diensten technologen toelaten hun focus op het uitbreiden van de waarde van hun intellectuele eigendom te houden. Dit blijft voor mij een wonderlijke reis zijn, vol met unieke leergelegenheden.
De engineer in mij wilde recentelijk of er een situatie bestond waarin ik een secundaire voordelen kon vinden voor een bestaand concept dat ik eerder had besproken. Met andere woorden, kon ik een ander voordeel met hetzelfde niveau van impact ontdekken als het originele oudersolution eerder erkend werd?
Voor dit artikel wilde ik dieper gaan kijken inGraphQL om wat ik kon vinden.
In mijn artikel “When It’s Time to Give REST a Rest” heb ik geprobeerd uit te leggen dat GraphQL in sommige wereldwijde scenario’s een betere keuze is dan een RESTful service. We hebben gezien hoe je een GraphQL-API kunt bouwen en implementeren met Apollo Server.
In deze volgende post plan ik mijn kennis van GraphQL te verhogen door langs abonnees voor real-time gegevensophaling te lopen. We zullen ook een WebSocket-dienst bouwen om de abonnees te kunnen consumeren.
Herhaal: Customer 360 Use Case
Mijn vorige artikel richtte zich op een Customer 360 use case, waarbij klanten van mijn fictieve bedrijf de volgende gegevensverzamelingen behouden:
- Klantgegevens
- Adresgegevens
- Kenmerken van contact
- Crediteigenschappen
Een grote voordeel van GraphQL is dat een enkele GraphQL-verzoek alle noodzakelijke gegevens voor een klantentoken (unieke identiteit) kunnen ophalen.
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
}
Met een RESTful aanpak om de enkele (360) weergave van de klant te verkrijgen, zou het vereist zijn dat meerdere verzoeken en antwoorden samengeknepen moesten worden. GraphQL biedt ons een performantere oplossing.
Level Up Doelstellingen
Om in elk aspect van het leven vooruit te komen, moet men nieuwe doelstellingen behalen. Voor mijn eigen doelstellingen hierbij betekent dit:
- begrijpen en de waardepropositie van
abonnementen
binnen GraphQL implementeren - Gebruik maken van een WebSocket-implementatie om een GraphQL-abonnement te consumeren
Het idee van het gebruik van abonnementen in plaats van queries en mutaties binnen GraphQL is de voorkeur methode wanneer de volgende voorwaarden worden vervuld:
- Kleine, inkrementele wijzigingen aan grote objecten
- Laaglatente, real-time updates (bijvoorbeeld in een chatapplicatie)
Dit is belangrijk omdat het implementeren van abonnementen binnen GraphQL niet eenvoudig is. Niet alleen moet de onderliggende server worden bijgewerkt, maar ook de applicatie die de abonnementen consumeert vereist een opnieuw ontwerp.
gelukkig is het geval dat we achter het opvolgen van onze Customer 360-voorbeeld een uitstekende kandidaat voor abonnementen is. Ook zullen we een WebSocket-aanpak gebruiken om deze abonnementen te laten werken.
Net zoals eerder zal ik doorgaan met het gebruik van Apollo.
Level Up Met Abonnementen Creds
Eerst moeten we de noodzakelijke bibliotheken installeren om abonnementen te ondersteunen met mijn Apollo GraphQL-server:
npm install ws
npm install graphql-ws @graphql-tools/schema
npm install graphql-subscriptions
Met deze items geïnstalleerd, richtte ik mijn focus op het bijwerken van het bestand index.ts
uit mijn originele repository om de constante typedefs
uit te breiden met de volgende informatie:
type Subscription {
creditUpdated: Credit
}
Ik heb ook een constante aangemaakt om een nieuwe PubSub
-instantie te huisvesten en een voorbeeldabonnement gemaakt dat we later zullen gebruiken:
const pubsub = new PubSub();
pubsub.publish('CREDIT_BALANCE_UPDATED', {
creditUpdated: {
}
});
Ik heb de bestaande resolvers opgeschoond en een nieuwe Subscription
toegevoegd voor deze nieuwe gebruiksscenario:
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']),
}
}
};
Ik hervormde vervolgens de serverconfiguratie en introduceerde het ontwerp voor abonnementen:
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`);
});
Om klantengerichte updates te simuleren, maakte ik de volgende methode aan om het creditbalans elke vijf seconden te verhogen met $50 terwijl de dienst actief is. Zodra het creditlimiet van $10.000 wordt bereikt (of overschreden), reset ik het balans terug naar $2.500, simulerend dat een balanceringbetaal is gedaan.
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();
Het volledige bestand index.ts
kan gevonden worden hier.
Deploy naar Heroku
Met de dienst klaar, is het nu tijd om de dienst te deployen zodat we met deze kunnen interacteren. Aangezien Heroku het laatst goed werkte (en het makkelijk voor mij is om te gebruiken), laten we die aanpak behouden.
Om te beginnen, moest ik de volgende commando’s van de Heroku CLI uitvoeren:
$ 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
Het commando voegde automatisch het door Heroku gebruikte repository toe als een externe:
$ git remote
heroku
origin
ALS ik in mijn voorgaande artikel noteerde, schakelt Apollo Server Apollo Explorer uit in productieomgevingen. Om Apollo Explorer beschikbaar te houden voor onze behoeften, moest ik de omgevingsvariabele NODE_ENV
instellen op development. Ik deed dat met de volgende CLI-opdracht:
$ heroku config:set NODE_ENV=development
Setting NODE_ENV and restarting jvc-graphql-server-sub... done, v3
NODE_ENV: development
Ik was klaar om mijn code naar Heroku te deployen:
$ git commit --allow-empty -m 'Deploy to Heroku'
$ git push heroku
Een snelle kijk op het Heroku Dashboard toonde aan dat mijn Apollo Server zonder problemen draait:
In de Instellingen sectie vond ik de Heroku-app URL voor deze service-instantie:
https://jvc-graphql-server-sub-1ec2e6406a82.herokuapp.com/
- Merk op: Deze link zal niet langer in dienst zijn bij de publicatie van dit artikel.
Voorlopig kon ik graphql
toevoegen aan deze URL om Apollo Server Studio te starten. Dit liet mij de abonnementen zien die zoals verwacht functioneerden:
Bekijk de Subscription-responses aan de rechterkant van het scherm.
Leveling Up With WebSocket Skillz
We kunnen de WebSocket-ondersteuning en de mogelijkheden van Heroku gebruiken om een implementatie te maken die de door ons gemaakte abonnementen gebruikt.
In mijn geval maak ik een bestand index.js met de volgende inhoud. In het algemeen werd dit een WebSocket-client aanmaken en ook een dummy HTTP-service die ik kon gebruiken om te valideren of de client actief was:
import { createClient } from "graphql-ws";
import { WebSocket } from "ws";
import http from "http";
// Maak een dummy HTTP-server aan om te binden aan 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);
}
// Abonneer op de creditUpdated-abonnement
client.subscribe(
{
query,
},
{
next: (data) => handleCreditUpdated(data.data.creditUpdated),
error: (err) => console.error('Subscription error:', err),
complete: () => console.log('Subscription complete'),
}
);
De volledige index.js
bestand kan gevonden worden hier.
We kunnen deze eenvoudige Node.js-toepassing ook naar Heroku deployen, zorgende ervoor dat we de omgevingsvariabele GRAPHQL_SUBSCRIPTION_HOST
instellen op de Heroku-app URL die we eerder gebruikten.
Ik heb ook het volgende Procfile
aangemaakt om aan Heroku te laten weten hoe mijn app moet worden opgestart:
web: node src/index.js
Vervolgens heb ik een nieuwe Heroku-app aangemaakt:
$ 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
Vervolgens heb ik de GRAPHQL_SUBSCRIPTION_HOST
omgevingsvariabele ingesteld om naar mijn draaiende GraphQL-server te verwijzen:
$ heroku --app jvc-websocket-example \
config:set \
GRAPHQL_SUBSCRIPTION_HOST=ws://jvc-graphql-server-sub-1ec2e6406a82.herokuapp.com/graphql
Op dit moment zijn we klaar om ons code naar Heroku te deployen:
$ git commit --allow-empty -m 'Deploy to Heroku'
$ git push heroku
Zodra de WebSocket-client start, kunnen we zijn status zien in de Heroku Dashboard:
Door de logboeken binnen het Heroku Dashboard voor het jvc-websocket-example
-exemplaar te bekijken, kunnen we de meerdere updates aan de balance
-eigenschap van de jvc-graphql-server-sub
-service zien. In mijn demo was ik zelfs in staat om het geval te vangen waarin de saldo tot nul was gebracht, simulerend dat een betaling werd gedaan:
In de terminal kunnen we dezelfde logboeken bereiken met de CLI-opdracht heroku logs.
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]: }
Niet alleen hebben we een GraphQL-dienst met een abonnementenimplementatie die draait, maar we hebben nu ook een WebSocket-client die deze updates consumeert.
Conclusie
Mijn lezers zullen wellicht onthouden dat ik een persoonlijke missieverklaring heb, die ik denk dat hij kan worden toegepast op elke IT-professional:
“Focuser uw tijd op het leveren van functionaliteit die de waarden van uw intellectuele eigendom uitbreidt.gebruik frameworks, producten en diensten voor alles anders.”
— J. Vester
In deze diepgaande zoektocht in GraphQL-abonnementen hebben we met succes updates gehaald van een Apollo Server die draait op Heroku, door middel van een andere dienst die ook op Heroku draait — een Node.js-gebaseerde applicatie die WebSockets gebruikt. Door middel van lichtgewicht abonnementen hebben we geëvenabled om query’s uit te voeren voor niet-veranderende data, maar zijn we eenvoudig geabonneerd op het ontvangen van creditbalansupdates zoals die plaatsvinden.
In de introductie heb ik gezegd dat ik zoek deed naar een extra waardeprincipe binnen een onderwerp dat ik eerder had geschreven over. GraphQL-abonnementen zijn een uitstekend voorbeeld van wat ik bedoende want het staat consumenten toe onmiddellijke updates te ontvangen zonder dat nodig zijn om queries uit te voeren op de brongegevens. Dit zal de gebruikers van de Customer 360 data erg exciteren, omdat ze kunnen dat ze live updates kunnen ontvangen zoals die plaatsvinden.
Heroku is een ander voorbeeld dat aan mijn missieverklaring blijft vasthouden door een platform aan te bieden dat mij in staat stelt snel oplossingen te prototyperen met behulp van een CLI en standaard Git-commando’s. Dit geeft mij niet alleen een gemakkelijke manier om mijn abonnementen use case te presenteren maar ook om een consument te implementeren die WebSockets gebruikt.
Als u geïnteresseerd bent in de broncode voor dit artikel, kijk dan eens in mijn repositories op GitLab:
Ik voel me verzekerd als ik zeg dat mijn vaardigheden met GraphQL door deze poging gelukkig zijn verbeterd. Deze reis was nieuw en uitdagingend voor me — en ook veel plezierig!
Ik plannen om de volgende stap te nemen in de authenticatie, die hoopvolle een andere kans biedt om aan de slag te gaan met GraphQL en Apollo Server. Blijf op de hoogte!
Hou vast aan een prachtig dag!
Source:
https://dzone.com/articles/leveling-up-my-graphql-skills-real-time-subscriptions