Introductie
Wanneer u iets over internet probeert te leren
ontwikkeling, merken we meestal dat front-end aanzienlijk toegankelijker is dan back-end. Daar zijn veel redenen voor, vooral het gevoel van directe feedback dat ontstaat als je een bepaald element van een pagina in de code wijzigt en merkt dat de wijziging op de website wordt toegepast. Deze feedback is vaak nuttig voor beginners, omdat ze hierdoor hun code kunnen aanpassen en van hun fouten kunnen leren. Helaas is dit niet het geval met de backend: vaak wordt er een aanzienlijke hoeveelheid werk besteed aan het vooraf opzetten van de omgeving en het installeren van de afhankelijkheden die nodig zijn om een eenvoudig “Hallo Wereld”-bericht op de terminal te laten verschijnen. Gelukkig is dit niet het geval. Er wordt voortdurend veel vooruitgang geboekt in de open-sourcegemeenschap bij het faciliteren van het ontwikkelingsproces van frameworks, en het verbeteren van de ervaring van de ontwikkelaar. NodeJs is hiervan een goed voorbeeld. Dit framework is een zeer capabele technologie die het schrijven op de server mogelijk maakt code in Javascript handig, en biedt een verscheidenheid aan ingebouwde tools en functies die het onderscheiden van zijn concurrenten. In dit artikel zullen we NodeJs en zijn ecosysteem verkennen, met een praktische aanpak, en een volledig functioneel project bouwen.
Wat gaan we bouwen?
ToDo-applicaties zijn een ideaal project voor beginners die front-end-ontwikkeling leren. Daarom hebben we besloten een Todo-lijst-API te bouwen. Dit zal ons in staat stellen om datapersistentie aan onze interface toe te voegen, en ons de mogelijkheid geven om deze data te manipuleren (door taken toe te voegen, bij te werken, te verwijderen, enz...).
De definitieve code kun je [hier] vinden (https://github.com/Zineeddine998/workshop_todo_application).
Onze hulpmiddelen
We zullen voor dit project een vereenvoudigde tech-stack gebruiken. Het kan worden beschouwd als een minimale versie van veel tools die je in echte projecten aantreft, met als reden dat de ideeën op een hoger niveau hetzelfde zijn. De details van de implementatie en de keuze van een specifiek hulpmiddel boven een ander zijn niet belangrijk om aan de slag te gaan.
-
NodeJs is, zoals we al zeiden, een van de meest populaire Javascript-frameworks voor het bouwen van server-side applicaties.
-
ExpressJs is een minimaal Javascript-framework dat bovenop NodeJS wordt gebruikt. Het versnelt het ontwikkelingsproces door gebruik te maken van veel ingebouwde functies. Het wordt ook gebruikt als een manier om de ontwikkelingspraktijken in NodeJS-projecten te standaardiseren om het gebruik ervan voor ingenieurs te vergemakkelijken.
-
LowDB is een eenvoudige database in het geheugen. De eenvoud ervan stelt ons in staat om te laten zien hoe we met een database kunnen communiceren in een NodeJs-project, zonder dat we ons bezig hoeven te houden met meer geavanceerde onderwerpen zoals implementaties en configuraties.
Nu we alle tools hebben geïdentificeerd die we zullen gebruiken, gaan we naar onze toetsenborden en beginnen met coderen!
Installatie
Node is beschikbaar op alle platforms. Alle installatiehandleidingen zijn te vinden op de officiële website. Windows-gebruikers moeten ervoor zorgen dat ze node pad naar omgevingsvariabelen toevoegen, zodat deze op de opdrachtregel kan worden gebruikt.
We hebben ook NPM geïnstalleerd nodig. Npm is de standaard pakketbeheerder voor NodeJs. Het zal ons in staat stellen onze projectafhankelijkheden te beheren. De installatiehandleiding vindt u hier.
Projectinitialisatie
Ga naar de link en kloon het starterproject:
Dit is een eenvoudige startersrepository voor ons project. Het bevat alle afhankelijkheden die we zullen gebruiken, samen met de projectbestandsstructuur. We zullen elk element uitleggen zodra het is bereikt. Open uw terminal, navigeer naar het pad van het project en voer de opdracht uit:
npm install
Hiermee worden alle afhankelijkheden van het project geïnstalleerd dat is opgegeven in het bestand package.json. package.json is het bestand dat zich in de root van elk Javascript/NodeJs-project bevindt. Het bevat metagegevens over dit laatste en wordt gebruikt om alle projectafhankelijkheden, scripts en versies te beheren.
Nadat alle afhankelijkheden zijn geïnstalleerd, kunnen we onze applicatie starten:
npm run start
“start” is een script dat we in het pakket hebben gespecificeerd. json-bestand. Het specificeert het invoerbestand voor onze applicatie, in ons geval app.js.
Het volgende bericht zou nu in uw terminal moeten verschijnen:
Dit betekent dat onze server succesvol is opgestart en luistert naar verzoeken die naar poort 3000 worden verzonden. Laten we naar app.js kijken en uitleggen wat hier gebeurt:
App.js is ons projectinvoerbestand (en het enige op dit moment). We instantiëren een uitdrukkelijke applicatie met de naam app, specificeren dat alle verzoeken met de http-methode "GET" en het subpad '/' via deze route zullen worden afgehandeld, en geven een functie door die middleware wordt genoemd en die het verzoek en het antwoordobject opneemt als parameters. Dit is van cruciaal belang, omdat het verzoek alle informatie bevat die nodig is om het te kunnen verwerken (parameters, hoofdtekst van het verzoek, verzoekheaders, enz.), en het antwoordobject het object is dat naar de client wordt teruggestuurd. We beginnen met het sturen van het bericht ‘Hallo wereld’. Daarna laten we onze applicatie luisteren naar alle inkomende verzoeken op de opgegeven poort (in ons geval 3000) en loggen we het bericht "Luisteren naar poort 3000" om aan te geven dat onze applicatie actief is en klaar is om verzoeken te ontvangen.
Open uw terminal en typ in de koppelingsbalk “localhost:3000/” en druk op Enter. Dit is het opgegeven pad dat we kunnen gebruiken om onze server lokaal te bereiken. U ontvangt het volgende bericht:
Databaseconfiguratie
Lowdb is een open-sourcedatabase die gemakkelijk te gebruiken is en geen specifieke configuratie vereist. Het algemene idee hierachter is om alle gegevens in een lokaal json-bestand op te slaan. Nadat LowDB is geïnstalleerd (wat is gedaan toen we alle afhankelijkheden installeerden), kunnen we de volgende code toevoegen aan db.js:
Deze code lijkt veel op de code die te vinden is in de officiële documentatie van LowDB. We hebben het een beetje aangepast voor ons eigen gebruik. Laten we het regel voor regel uitleggen:
De eerste paar regels zijn bedoeld voor het importeren van de benodigde afhankelijkheden. “join” is een hulpprogrammafunctie die beschikbaar is in de module “path”. Het is een van de kernmodules van NodeJs die veel methoden biedt voor het omgaan met en verwerken van paden. “Low” en “JSONFile'' zijn de twee klassen die door LowDB worden weergegeven. De eerste maakt de json-bestandsinstantie die onze gegevens zal bevatten. De tweede creëert de daadwerkelijke database-instantie die erop zal reageren. Ten slotte is “lodash” een van de meest gebruikte javascript-bibliotheken die een breed scala aan hulpprogramma's biedt voor algemene programmeertaken. We voegen het toe aan onze database-instantie zodat we de geavanceerde methoden kunnen gebruiken om onze gegevens te verwerken.
Eerst specificeren we het pad voor het bestand db.json. Het is het bestand dat onze gegevens zal bevatten en zal worden doorgegeven aan LowDB. Als het bestand niet op het opgegeven pad wordt gevonden, zal LowDB er een maken.
Vervolgens geven we het bestandspad door aan de LowDB-adapter en geven we het door aan onze nieuwe LowDB-database-instantie. De variabele “db” kan vervolgens worden gebruikt om met onze database te communiceren. Zodra de database-instantie is gemaakt, lezen we uit het json-bestand met behulp van db.read(). Hierdoor wordt het veld 'gegevens' in onze database-instantie ingesteld, zodat we toegang hebben tot de database-inhoud. Merk op dat we deze regel voorafgingen met "afwachten". Dit is om te specificeren dat het oplossen van deze instructie een onbekende hoeveelheid tijd kan duren, en dat het NodeJs-proces moet wachten op uitvoering voordat het verdergaat met de rest van de code. We doen dit omdat de leesbewerking geheugentoegang tot het opgegeven bestand vereist, en de tijd voor het uitvoeren van dit soort bewerkingen hangt af van de specificaties van uw machine.
Nu we toegang hebben tot het gegevensveld, stellen we het in als een object dat een lege array met berichten bevat, of beter gezegd: we controleren of het bestand eerdere gegevens bevat en stellen de lege array in als dit niet het geval is.
Ten slotte voeren we db.write() uit om de wijzigingen toe te passen die we in de gegevens hebben aangebracht, en exporteren we de database-instantie zodat deze in andere bestanden in ons project kan worden gebruikt.
Algemene verzoek-/antwoordworkflow
Beschouw het volgende diagram:
Het toont de algemene architectuur die wordt toegepast in een overvloed aan backend-applicaties die zijn gebouwd met NodeJs/Express. Als u de algemene workflow achter het afhandelen van een verzoek begrijpt, kunt u niet alleen NodeJs-applicaties bouwen en structureren, maar kunt u deze concepten ook overbrengen naar vrijwel elke technische stapel van uw keuze. We zullen de verschillende lagen verkennen die dit proces verstoren, en hun rol uitleggen:
## HTTP-verzoeklaag
Dit is de eerste laag in onze applicatie. Stel je het voor als een gateway die een breed scala aan verschillende verzoeken ontvangt van verschillende klanten. Elk verzoek wordt vervolgens geanalyseerd en doorgestuurd naar het speciale deel van de applicatie, zodat het kan worden afgehandeld.
-
Routers: hier hebben we het over Express-routers, maar dit concept is terug te vinden in veel backend-frameworks. Routers zijn een manier om de logische distributie in onze bedrijfslogica op onze code toe te passen, wat betekent dat elke set elementen met vergelijkbare kenmerken door dezelfde ingang wordt afgehandeld en van de rest van de sets kan worden gescheiden. Dit heeft het voordeel dat elk onderdeel van de code onafhankelijk wordt van de andere, en gemakkelijker te onderhouden en uit te breiden is. Meer specifiek, en als voorbeeld: alle verzoeken die voldoen aan de voorwaarden van het gedeelde URL-pad “/posts” worden door dezelfde router afgehandeld. Afhankelijk van hun http-methode (GET, POST, etc..), zal een andere controller worden gebruikt.
-
Controllers: een controller ontvangt gefilterde verzoeken van routers, past aanvullende verwerking toe en roept de geschikte servicemethoden op.
Bedrijfslogicalaag
Deze laag is uniek, afhankelijk van de specifieke gebruiksscenario's van de applicatie en de bedrijfslogica erachter.
-
Services: Services zijn een reeks methoden die de kernlogica van de applicatie bevatten. Ze communiceren ook met de database door het gebruik van ORMs/ODMs.).
-
Diensten van derden: veel moderne applicaties kiezen ervoor om een deel van de logica van de applicatie te delegeren aan speciale diensten die toegankelijk zijn via een API. Dit soort diensten kunnen betalingsafhandelingsdiensten, opslag van statische bestanden, meldingen en andere zijn.
-
ODM/ORM: ORM's en ODM's fungeren als tussenpersoon tussen de services en de database. Hun rol is het bieden van een abstractie op hoog niveau op een database, waardoor een ontwikkelaar code kan schrijven in de programmeertaal van zijn keuze in plaats van speciale databasetalen, zoals SQL.
Gegevenspersistentielaag
- Databases: zoals we eerder vermeldden, hebben bijna alle applicaties een vorm van datapersistentie nodig. Dit deel wordt afgehandeld door databases, en afhankelijk van de aard van de gegevens, de bedrijfslogica en vele andere overwegingen wordt de keuze voor een bepaalde database boven een andere als cruciaal beschouwd voor de efficiëntie en de schaalbaarheid van de applicatie.
Voorbeeld: een bericht toevoegen
Nu we het algemene idee achter de architectuur begrijpen, laten we het toepassen op ons eenvoudige voorbeeld. We zullen de functie implementeren van het toevoegen van een todo-post aan onze applicatie. Laten we aannemen dat elk bericht een unieke ID heeft waarmee we het later in onze database kunnen identificeren, een titel die uit een string bestaat en een volgorde van het type geheel getal. Volgens ons diagram beginnen we met het implementeren van de router. Voeg de volgende code toe aan het index.js-bestand:
Dit is ons routerbestand. We importeren express en de “addPost” -methode van onze controller (we zullen deze binnenkort implementeren), creëren een exemplaar van de express router en binden de addPost-methode aan onze router - wat betekent dat voor elk verzoek met het rootpad en de http methode “POST”, de “addPost” methode zal worden aangeroepen om dit af te handelen.
Voordat we onze methode in de controller implementeren, verwijzen we naar de nieuwe router in ons hoofdbestand app.js en specificeren we het pad als “/posts”: alle routes met de opgegeven paden worden doorgestuurd naar deze router, zodat deze kan worden afgehandeld door de verschillende controllermethoden:
We importeren de router en noemen deze ‘posts’. app.use(“/posts”,..) betekent dat alle verzoeken met het subpad “/posts”, ongeacht hun http-methode, naar de opgegeven router worden gerouteerd.
Andere wijzigingen in app.js zijn onder meer het importeren van het databaseconfiguratiebestand zodat het kan worden uitgevoerd, en het gebruik van express.json() als middleware om ons toegang te geven tot het verzoeklichaamsobject.
Nu onze routes zijn ingesteld, kunnen we de “addPost” -methode toevoegen aan het controller.js-bestand:
“addPost” is een middleware-functie die het verzoek, de responsobjecten en de volgende functie als parameters gebruikt. Wanneer de volgende functie wordt aangeroepen, gaat het proces naar de volgende middleware in de keten of beëindigt het verzoek. In de code van de methode halen we de titel en de volgorde uit de hoofdtekst van het verzoek en geven deze als parameters door aan de servicefunctie “createPost”. Deze functie neemt de berichtkenmerken, maakt een nieuw bericht en retourneert dit. Zodra het nieuwe bericht is aangemaakt, sturen we het samen met de statuscode 200 terug naar de klant, wat betekent dat het verzoek succesvol is geweest. Het zal je misschien opvallen dat onze code in een try/catch-blok wordt geplaatst om eventuele onverwachte fouten op te vangen en door te geven aan de volgende middleware. Het wordt als de beste praktijk beschouwd om aan alle routers een middleware voor foutafhandeling te koppelen die de fout ophaalt en een betekenisvol foutbericht terugstuurt naar de client.
Het enige dat nu nog overblijft is het implementeren van de functie “createPost” in service.js:
Zoals we eerder vermeldden bij het uitleggen van de verschillende lagen van de architectuur, interageert de servicelaag met de dataopslagoplossing door het gebruik van ORM/ODM. In ons voorbeeld hoeven we echter geen aparte ORM te gebruiken, omdat Lowdb ingebouwde ondersteuning voor Javascript heeft. Alle details over de syntaxis ervan zijn te vinden in de documentatie.
De “createPost”-methode ontvangt titel en volgorde als parameters en gebruikt deze om het post-object te maken. Voor de unieke ID gebruiken we een speciale bibliotheek genaamd “nanoid”, die een unieke reeks karakters genereert. We voegen het nieuwe bericht toe aan de posts-array in de database en schrijven deze wijzigingen; het nieuwe bericht wordt vervolgens geretourneerd door de functie.
Nu “createPost” gereed is, is de functie voor het toevoegen van berichten nu voltooid en actief. We testen het met Postman, een populaire tool voor het testen van API’s:
We selecteren “POST” als de http-methode voor het verzoek, samen met het opgegeven URL-pad “localhost:3000/posts”. We voegen de titel en de volgorde toe als json-formaat in de body-sectie en verzenden het verzoek. Zoals hierboven weergegeven, ontvangen we de status 200 OK samen met het nieuw gemaakte bericht.
Conclusie
In dit project zijn veel concepten en ideeën onderzocht: we hebben besproken hoe we onze projectomgeving moeten installeren en instellen, we hebben geleerd hoe we LowDB kunnen configureren voor lokale gegevenspersistentie, we hebben de algemene architectuur van NodeJS/Express-backend-applicaties verkend en we hebben gezien hoe we pas het toe in een eenvoudig voorbeeld. Ten slotte hebben we onze applicatie getest met Postman.
De bedoeling hier was om een vereenvoudigde versie bloot te leggen van alles wat komt kijken bij het bouwen van moderne backend-applicaties. Zoals we eerder zagen, is NodeJs een krachtige tool waarmee we eenvoudige en complexe API’s kunnen bouwen. Gecombineerd met zijn rijke ecosysteem aan raamwerken, zoals Express en een overvloed aan tools en bibliotheken voor vrijwel elk gebruik, is het een legitieme oplossing voor moderne backend-ontwikkeling - een oplossing die we aanbevelen om te leren en te beheersen.