V tej seriji člankov bomo razvili prototip spletnega mesta s statično vsebino. Ustvaril bo dnevno posodobljene, preproste statične strani HTML za priljubljena skladišča GitHub za sledenje njihovim najnovejšim izdajam. Statični okviri za ustvarjanje spletnih strani imajo odlične funkcije, da to dosežemo - uporabili bomo Gatsby.js, enega najbolj priljubljenih.
V Gatsbyju obstaja veliko načinov za zbiranje podatkov za prednji del, ne da bi imel zaledni (brez strežnika), Brezglave platforme za upravljanje vsebin in Izvorni vtičniki Gatsby med njimi. Vpeljali pa bomo zaledje za shranjevanje osnovnih informacij o skladiščih GitHub in njihovih najnovejših izdajah. Tako bomo imeli popoln nadzor nad hrbtom in prednjim delom.
Zajemal bom tudi nabor orodij za sprožitev dnevne posodobitve vaše aplikacije. Lahko ga sprožite tudi ročno ali kadarkoli se zgodi kakšen poseben dogodek.
Naša vmesna aplikacija se bo izvajala na Netlify, vmesna aplikacija pa bo delovala na Heroku z brezplačnim načrtom. Periodično bo spala : 'Ko nekdo dostopa do aplikacije, bo upravitelj dyno samodejno prebudil spletni dyno za zagon vrste spletnega procesa.' Torej, lahko ga zbudimo preko AWS Lambda in AWS CloudWatch. V času pisanja tega poročila je to najbolj stroškovno učinkovit način, kako imeti prototip na spletu 24/7.
Da bi bili članki osredotočeni na eno temo, ne bom obravnaval preverjanja pristnosti, preverjanja veljavnosti, razširljivosti ali drugih splošnih tem. Šifrirni del tega članka bo čim bolj preprost. Pomembnejša je struktura projekta in uporaba pravilnega nabora orodij.
V tem prvem delu serije bomo razvili in uvedli našo zaledno aplikacijo. V drugi del , bomo razvili in uvedli našo front-end aplikacijo ter sprožili vsakodnevne gradnje.
Zaledna aplikacija bo napisana v Node.js (ni obvezno, ampak zaradi poenostavitve), vsa komunikacija pa bo prek API-jev REST. V tem projektu ne bomo zbirali podatkov s sprednje strani. (Če vas to zanima, si oglejte Obrazci Gatsby .)
Najprej bomo začeli z izvajanjem preprostega ozadja API-ja REST, ki razkriva CRUD operacije zbirke repozitorija v našem MongoDB. Nato bomo razporedili opravilo cron, ki zajema GitHub API v4 (GraphQL), da posodobi dokumente v tej zbirki. Potem bomo vse to razporedili v oblak Heroku. Na koncu bomo sprožili obnovo sprednjega dela na koncu našega dela cron.
V drugem članku se bomo osredotočili na izvajanje createPages
OGNJ . Vse zbirke bomo zbrali na zadnji strani in ustvarili eno samo domačo stran, ki vsebuje seznam vseh skladišč ter stran za vsak vrnjeni dokument repozitorija. Potem bomo namesti naš prednji del na Netlify .
Ta del ni obvezen, če vaša aplikacija ne bo spala. V nasprotnem primeru morate biti prepričani, da je vaš zadnji del v času posodabljanja repozitorijev pripravljen in deluje. Kot rešitev lahko ustvarite urnik cron v programu AWS CloudWatch 10 minut pred dnevno posodobitvijo in ga povežete kot sprožilec GET
metoda v AWS Lambda. Dostop do zaledne aplikacije bo prebudil primerek Heroku. Več podrobnosti bo na koncu drugega članka.
Tu je arhitektura, ki jo bomo uvedli:
okvir spletne aplikacije node js
Predvidevam, da imajo bralci tega članka znanje na naslednjih področjih:
Dobro je tudi, če veste:
Potopimo se v izvedbo zadnjega dela. Razdelili ga bomo na dve nalogi. Prvi pripravlja končne točke REST API in jih veže na našo zbirko skladišč. Drugi je izvajanje opravila cron, ki porabi API GitHub in posodobi zbirko.
Express bomo uporabili za ogrodje spletnih aplikacij, Mongoose pa za povezavo MongoDB. Če poznate Express in Mongoose, boste morda lahko preskočili na 2. korak.
(Po drugi strani pa, če potrebujete več seznanitve z Expressom, si lahko ogledate uradni vodnik za zagon Express ; če niste na Mongoose, uradni začetni vodnik Mongoose mora biti v pomoč.)
Hierarhija datotek / map našega projekta bo preprosta:
Podrobneje:
env.config.js
je konfiguracijska datoteka spremenljivk okoljaroutes.config.js
je za preslikavo ostalih končnih točkrepository.controller.js
vsebuje metode za delo na našem modelu repozitorijarepository.model.js
vsebuje shemo MongoDB repozitorija in operacij CRUDindex.js
je razred inicializatorjapackage.json
vsebuje odvisnosti in lastnosti projektaZaženi npm install
(ali yarn
, če imate nameščeno Prejo) po dodajanju teh odvisnosti v package.json
:
{ // ... 'dependencies': { 'body-parser': '1.7.0', 'express': '^4.8.7', 'moment': '^2.17.1', 'moment-timezone': '^0.5.13', 'mongoose': '^5.1.1', 'node-uuid': '^1.4.8', 'sync-request': '^4.0.2' } // ... }
Naš env.config.js
datoteka ima samo port
, environment
(dev
ali prod
) in mongoDbUri
lastnosti za zdaj:
module.exports = ;
routes.config.js
vsebuje preslikave zahtev in bo poklical ustrezno metodo našega krmilnika:
const RepositoryController = require('../controller/repository.controller'); exports.routesConfig = function(app) { app.post('/repositories', [ RepositoryController.insert ]); app.get('/repositories', [ RepositoryController.list ]); app.get('/repositories/:id', [ RepositoryController.findById ]); app.patch('/repositories/:id', [ RepositoryController.patchById ]); app.delete('/repositories/:id', [ RepositoryController.deleteById ]); };
The repository.controller.js
datoteka je naša plast storitve. Njegova odgovornost je, da pokliče ustrezno metodo našega modela repozitorija:
const RepositoryModel = require('../model/repository.model'); exports.insert = (req, res) => { RepositoryModel.create(req.body) .then((result) => { res.status(201).send({ id: result._id }); }); }; exports.findById = (req, res) => { RepositoryModel.findById(req.params.id) .then((result) => { res.status(200).send(result); }); }; exports.list = (req, res) => { RepositoryModel.list() .then((result) => { res.status(200).send(result); }) }; exports.patchById = (req, res) => { RepositoryModel.patchById(req.params.id, req.body) .then(() => { res.status(204).send({}); }); }; exports.deleteById = (req, res) => { RepositoryModel.deleteById(req.params.id, req.body) .then(() => { res.status(204).send({}); }); };
repository.model.js
obravnava povezavo MongoDb in operacije CRUD za model repozitorija. Polja modela so:
owner
: Lastnik skladišča (podjetje ali uporabnik)name
: Ime skladiščacreatedAt
: Datum zadnje izdaje izdajeresourcePath
: Zadnja pot izdajetagName
: Zadnja oznaka za izdajoreleaseDescription
: Opombe ob izdajihomepageUrl
: Domači URL projektarepositoryDescription
: Opis skladiščaavatarUrl
: URL avatarja lastnika projektaconst Mongoose = require('mongoose'); const Config = require('../config/env.config'); const MONGODB_URI = Config.mongoDbUri; Mongoose.connect(MONGODB_URI, { useNewUrlParser: true }); const Schema = Mongoose.Schema; const repositorySchema = new Schema({ owner: String, name: String, createdAt: String, resourcePath: String, tagName: String, releaseDescription: String, homepageUrl: String, repositoryDescription: String, avatarUrl: String }); repositorySchema.virtual('id').get(function() { return this._id.toHexString(); }); // Ensure virtual fields are serialised. repositorySchema.set('toJSON', { virtuals: true }); repositorySchema.findById = function(cb) { return this.model('Repository').find({ id: this.id }, cb); }; const Repository = Mongoose.model('repository', repositorySchema); exports.findById = (id) => { return Repository.findById(id) .then((result) => { if (result) { result = result.toJSON(); delete result._id; delete result.__v; return result; } }); }; exports.create = (repositoryData) => { const repository = new Repository(repositoryData); return repository.save(); }; exports.list = () => { return new Promise((resolve, reject) => { Repository.find() .exec(function(err, users) { if (err) { reject(err); } else { resolve(users); } }) }); }; exports.patchById = (id, repositoryData) => { return new Promise((resolve, reject) => { Repository.findById(id, function(err, repository) { if (err) reject(err); for (let i in repositoryData) { repository[i] = repositoryData[i]; } repository.save(function(err, updatedRepository) { if (err) return reject(err); resolve(updatedRepository); }); }); }) }; exports.deleteById = (id) => { return new Promise((resolve, reject) => { Repository.deleteOne({ _id: id }, (err) => { if (err) { reject(err); } else { resolve(err); } }); }); }; exports.findByOwnerAndName = (owner, name) => { return Repository.find({ owner: owner, name: name }); };
To je tisto, kar imamo po prvi odobritvi: Povezava MongoDB in naše operacije REST .
Našo aplikacijo lahko zaženemo z naslednjim ukazom:
node index.js
Za testiranje pošljite zahteve na localhost:3000
(z uporabo npr. poštarja ali curl-a):
Prispevek: http: // localhost: 3000 / repozitorije
Telo:
{ 'owner' : 'facebook', 'name' : 'react' }
Pridobite: http: // localhost: 3000 / repozitorije
Pridobite: http: // localhost: 3000 / repozitorije /: id
Obliž: http: // localhost: 3000 / repozitorije /: id
Telo:
{ 'owner' : 'facebook', 'name' : 'facebook-android-sdk' }
S tem časom je čas za avtomatizacijo posodobitev.
V tem delu bomo konfigurirali preprosto opravilo cron (ki se bo začelo ob polnoči UTC) za posodobitev skladišč GitHub, ki smo jih vstavili v našo bazo podatkov. Dodali smo samo owner
in name
parametrov samo v zgornjem primeru, vendar sta nam ti dve polji dovolj za dostop do splošnih informacij o danem repozitoriju.
Da bi posodobili svoje podatke, moramo uporabiti GitHub API. V tem delu je najbolje, da ste seznanjeni GraphQL in v4 API-ja GitHub .
Tudi mi moramo ustvarite žeton za dostop GitHub . Minimalni zahtevani obseg za to so:
To bo ustvarilo žeton in z njim lahko pošljemo zahteve na GitHub.
Zdaj se vrnimo k naši kodi.
V package.json
imamo dve novi odvisnosti:
'axios': '^0.18.0'
je odjemalec HTTP, zato lahko pošiljamo zahteve za API GitHub'cron': '^1.7.0'
je cron načrtovalec opravilKot običajno zaženite npm install
ali yarn
po dodajanju odvisnosti.
Tudi v config.js
bomo potrebovali dve novi lastnosti:
'githubEndpoint': 'https://api.github.com/graphql'
'githubAccessToken': process.env.GITHUB_ACCESS_TOKEN
(spremenljivko GITHUB_ACCESS_TOKEN
okolja boste morali nastaviti z lastnim žetonom za dostop)Ustvari novo datoteko pod controller
mapa z imenom cron.controller.js
. Preprosto bo poklical updateResositories
metoda repository.controller.js
ob predvidenem času:
const RepositoryController = require('../controller/repository.controller'); const CronJob = require('cron').CronJob; function updateDaily() { RepositoryController.updateRepositories(); } exports.startCronJobs = function () { new CronJob('0 0 * * *', function () {updateDaily()}, null, true, 'UTC'); };
Končne spremembe tega dela bodo v repository.controller.js
. Za kratkost ga bomo zasnovali tako, da bo hkrati posodobil vsa skladišča. Če pa imate veliko število skladišč, lahko presežete omejitve virov API-ja GitHub . V tem primeru boste morali to spremeniti tako, da bo deloval v omejenih serijah, razporejenih skozi čas.
Izvedba posodobitvene funkcije naenkrat bo videti tako:
async function asyncUpdate() { await RepositoryModel.list().then((array) => { const promises = array.map(getLatestRelease); return Promise.all(promises); }); } exports.updateRepositories = async function update() { console.log('GitHub Repositories Update Started'); await asyncUpdate().then(() => { console.log('GitHub Repositories Update Finished'); }); };
Na koncu bomo poklicali končno točko in posodobili model repozitorija.
The getLatestRelease
funkcija bo ustvarila poizvedbo GraphQL in poklicala API GitHub. Odgovor na to zahtevo bo nato obdelan v updateDatabase
funkcijo.
kako pišeš kodo
async function updateDatabase(responseData, owner, name) { let createdAt = ''; let resourcePath = ''; let tagName = ''; let releaseDescription = ''; let homepageUrl = ''; let repositoryDescription = ''; let avatarUrl = ''; if (responseData.repository.releases) { createdAt = responseData.repository.releases.nodes[0].createdAt; resourcePath = responseData.repository.releases.nodes[0].resourcePath; tagName = responseData.repository.releases.nodes[0].tagName; releaseDescription = responseData.repository.releases.nodes[0].description; homepageUrl = responseData.repository.homepageUrl; repositoryDescription = responseData.repository.description; if (responseData.organization && responseData.organization.avatarUrl) { avatarUrl = responseData.organization.avatarUrl; } else if (responseData.user && responseData.user.avatarUrl) { avatarUrl = responseData.user.avatarUrl; } const repositoryData = { owner: owner, name: name, createdAt: createdAt, resourcePath: resourcePath, tagName: tagName, releaseDescription: releaseDescription, homepageUrl: homepageUrl, repositoryDescription: repositoryDescription, avatarUrl: avatarUrl }; await RepositoryModel.findByOwnerAndName(owner, name) .then((oldGitHubRelease) => { if (!oldGitHubRelease[0]) { RepositoryModel.create(repositoryData); } else { RepositoryModel.patchById(oldGitHubRelease[0].id, repositoryData); } console.log(`Updated latest release: http://github.com${repositoryData.resourcePath}`); }); } } async function getLatestRelease(repository) { const owner = repository.owner; const name = repository.name; console.log(`Getting latest release for: http://github.com/${owner}/${name}`); const query = ` query { organization(login: '${owner}') { avatarUrl } user(login: '${owner}') { avatarUrl } repository(owner: '${owner}', name: '${name}') { homepageUrl description releases(first: 1, orderBy: {field: CREATED_AT, direction: DESC}) { nodes { createdAt resourcePath tagName description } } } }`; const jsonQuery = JSON.stringify({ query }); const headers = { 'User-Agent': 'Release Tracker', 'Authorization': `Bearer ${GITHUB_ACCESS_TOKEN}` }; await Axios.post(GITHUB_API_URL, jsonQuery, { headers: headers }).then((response) => { return updateDatabase(response.data.data, owner, name); }); }
Po drugem prevzemu bomo izvedli cron načrtovalnik za vsakodnevne posodobitve iz naših skladišč GitHub .
Z zadnjim delom smo skoraj končali. Toda zadnji korak je treba narediti po izvedbi čelnega dela, zato ga bomo obravnavali v naslednjem članku.
V tem koraku bomo našo aplikacijo namestili v Heroku, torej z njimi boste morali ustvariti račun če ga že nimate. Če svoj račun Heroku vežemo na GitHub, bomo veliko lažje neprekinjeno uvajali. V ta namen Svoj projekt gostujem na GitHubu .
Po prijavi v račun Heroku na nadzorni plošči dodajte novo aplikacijo:
Dajte mu nekaj edinstvenega imena:
Preusmerjeni boste v razdelek za razmestitev. Kot način uvajanja izberite GitHub, poiščite svoje skladišče in kliknite gumb »Poveži«:
Zaradi poenostavitve lahko omogočite samodejno razporeditev. Uvedel se bo vsakič, ko v repo GitHub potisnete predajo:
Zdaj moramo dodati vir MongoDB. Odprite zavihek Viri in kliknite »Poiščite več dodatkov«. (Osebno uporabljam mLab mongoDB.)
lokalni spletni strežnik raspberry pi
Namestite ga in vnesite ime aplikacije v polje za vnos »Od aplikacije do določbe«:
Na koncu moramo ustvariti datoteko z imenom Procfile
na korenski ravni našega projekta, ki določa ukaze, ki jih izvaja aplikacija, ko jo Heroku zažene.
Naš Procfile
je tako preprosto:
web: node index.js
Ustvari datoteko in jo objavi. Ko potisnete predajo, bo Heroku samodejno namestil vašo aplikacijo, ki bo dostopna kot https://[YOUR_UNIQUE_APP_NAME].herokuapp.com/
.
Če želimo preveriti, ali deluje, lahko pošljemo iste zahteve, kot smo jih poslali na localhost
.
Po naši tretji zavezi, tako bo videti naš repo .
Do zdaj smo izvedli Node.js / Express-based REST API na našem hrbtnem delu, posodabljalnik, ki uporablja GitHub-ov API, in opravilo cron, ki ga aktivira. Nato smo razporedili svoj zadnji konec, ki bo kasneje zagotovil podatke za naš generator statične spletne vsebine uporaba Herokuja s kavljem za neprekinjeno integracijo. Zdaj ste pripravljeni na drugi del , kjer implementiramo sprednji del in dokončamo aplikacijo!
Sorodno: 10 najpogostejših napak, ki jih naredijo razvijalci Node.js.Po objavi statične spletne strani vsebujejo enake podatke za vse seje. Na dinamičnih spletnih straneh je mogoče podatke sproti posodabljati.
Node.js je lahek, hiter, razširljiv, odprtokoden in ga dobro podpira njegova skupnost.
Node.js služi kot zaledno izvajalno okolje za gradnjo razširljivih, lahkih, asinhronih spletnih aplikacij, ki jih vodijo dogodki, z JavaScriptom.
Node.js uporablja isti jezik (JavaScript) za strežniško stran, ki se običajno uporablja v brskalniku. Je lahek in zasnovan za uporabo neblokirajočih V / I operacij med obdelavo zahtev.
Kot član priljubljenega sklada MEAN - MongoDB, Express.js, Angular in Node.js - je Node.js pomemben za razvoj visoko zmogljivih, razširljivih spletnih aplikacij z JavaScriptom.
Nekatere prednosti GraphQL vključujejo zbiranje le tistega, kar potrebujete od strežnika, pridobivanje več virov v eni zahtevi in dejstvo, da so njegovi API-ji samo-dokumentirani.
GraphQL omogoča hitro izdelavo prototipov in proizvodno uvajanje. Poleg tega uporablja eno končno točko za vse vire, kar olajša komunikacijo med odjemalcem in strežnikom.
Heroku je oblačna platforma, ki se osredotoča na racionalizacijo zagona in spreminjanja aplikacij.