Service Workers implementacija cache valdymui

Kas tie Service Workers ir kodėl turėtumėte susipažinti

Jei dar nesate susidūrę su Service Workers, tai greičiausiai dirbate su legacy projektais arba tiesiog nesekate naujausių web development tendencijų. Bet nesijaudinkite – niekada nevėlu pradėti. Service Workers iš esmės yra JavaScript failai, kurie veikia atskirame thread’e nuo pagrindinės svetainės ir gali perimti bei modifikuoti network užklausas. Skamba bauginančiai? Gal šiek tiek, bet realybėje tai vienas galingiausių įrankių moderniam web’ui.

Pagrindinis dalykas, kurį reikia suprasti – Service Worker veikia kaip proxy tarp jūsų aplikacijos ir tinklo. Jis gali interceptinti fetch užklausas, cache’inti resursus ir net leisti jūsų aplikacijai veikti offline. Tai nėra magiška bullet, kuri išspręs visas performance problemas, bet tinkamai implementavus gali drastiškai pagerinti user experience.

Vienas svarbiausių aspektų – Service Workers veikia tik per HTTPS (išskyrus localhost development). Tai saugumo reikalavimas, nes turite galimybę perimti ir modifikuoti network traffic’ą. Taip pat verta žinoti, kad jie neturi prieigos prie DOM – visą komunikaciją su page reikia daryti per postMessage API.

Cache strategijos: ne viena dydis tinka visiems

Kai pradėjau dirbti su Service Workers, didžiausia klaida buvo manyti, kad cache’insiu viską ir viskas bus super greita. Realybė – skirtingi resursai reikalauja skirtingų strategijų. Yra keletas pagrindinių pattern’ų, kuriuos verta žinoti:

Cache First strategija puikiai tinka statiniams resursams – CSS, JavaScript bundle’ams, šriftams, paveikslėliams. Čia logika paprasta: pirma žiūrime cache’e, jei nerandame – einame į network. Kartą užkrovę resursą, jis lieka cache’e ir užsikrauna akimirksniu. Problema – jei resursas pasikeičia serveryje, vartotojas gali nematyti atnaujinimų, kol neišvalys cache arba nepersirašysite Service Worker’io.

Network First geriau tinka dinaminiams turiniams – API atsakymams, naujienom, bet kokiam content’ui, kuris dažnai keičiasi. Pirma bandome gauti fresh data iš serverio, jei nepavyksta (offline arba lėtas internetas) – grąžiname iš cache. Taip užtikriname, kad vartotojas mato naujausią informaciją, bet vis tiek turi fallback’ą.

Stale While Revalidate – mano asmeniškai mėgstamiausia strategija daugeliui use case’ų. Grąžini iš cache (jei yra), bet tuo pačiu background’e darai network request ir atnaujini cache. Vartotojas gauna instant response, o sekantis apsilankymas jau turės fresh data. Idealus balansas tarp greičio ir aktualumo.

Praktinė implementacija: nuo nulio iki veikiančio sprendimo

Pradėkime nuo paprasto Service Worker registracijos. Jūsų main JavaScript faile (pvz., app.js) reikia tokio kodo:

„`javascript
if (‘serviceWorker’ in navigator) {
window.addEventListener(‘load’, () => {
navigator.serviceWorker.register(‘/service-worker.js’)
.then(registration => {
console.log(‘SW registered:’, registration);
})
.catch(error => {
console.log(‘SW registration failed:’, error);
});
});
}
„`

Čia tikriname ar naršyklė palaiko Service Workers (ne visos senos versijos palaiko), ir registruojame mūsų worker’į. Svarbu daryti tai po `load` event’o, kad neblokuotume pradinės page load.

Dabar pats Service Worker failas. Sukuriame `service-worker.js` projekto root’e:

„`javascript
const CACHE_NAME = ‘my-app-cache-v1’;
const urlsToCache = [
‘/’,
‘/styles/main.css’,
‘/scripts/app.js’,
‘/images/logo.png’
];

self.addEventListener(‘install’, event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log(‘Cache opened’);
return cache.addAll(urlsToCache);
})
);
});
„`

Install event’as įvyksta kai Service Worker pirmą kartą užregistruojamas. Čia pre-cache’iname kritinius resursus – tuos, kurie būtini aplikacijai veikti. Neperkraukite šito sąrašo – kuo daugiau failų, tuo ilgiau užtruks installation.

Fetch interceptavimas ir cache logika

Dabar pats įdomiausias dalykas – fetch event’o handling. Čia galime implementuoti mūsų cache strategijas:

„`javascript
self.addEventListener(‘fetch’, event => {
event.respondWith(
caches.match(event.request)
.then(response => {
// Cache hit – grąžiname response iš cache
if (response) {
return response;
}

return fetch(event.request).then(
response => {
// Tikriname ar gavome validų response
if(!response || response.status !== 200 || response.type !== ‘basic’) {
return response;
}

// Kloname response nes galime jį naudoti tik kartą
const responseToCache = response.clone();

caches.open(CACHE_NAME)
.then(cache => {
cache.put(event.request, responseToCache);
});

return response;
}
);
})
);
});
„`

Šitas kodas implementuoja Cache First strategiją. Bet realybėje norėsite kažko sudėtingesnio – skirtingų strategijų skirtingiems resursams. Štai kaip tai galima padaryti:

„`javascript
self.addEventListener(‘fetch’, event => {
const { request } = event;
const url = new URL(request.url);

// API calls – Network First
if (url.pathname.startsWith(‘/api/’)) {
event.respondWith(
fetch(request)
.then(response => {
const responseClone = response.clone();
caches.open(CACHE_NAME).then(cache => {
cache.put(request, responseClone);
});
return response;
})
.catch(() => {
return caches.match(request);
})
);
return;
}

// Static assets – Cache First
if (request.destination === ‘image’ ||
request.destination === ‘style’ ||
request.destination === ‘script’) {
event.respondWith(
caches.match(request)
.then(response => response || fetch(request))
);
return;
}

// Viskam kitam – Stale While Revalidate
event.respondWith(
caches.match(request)
.then(cachedResponse => {
const fetchPromise = fetch(request).then(networkResponse => {
caches.open(CACHE_NAME).then(cache => {
cache.put(request, networkResponse.clone());
});
return networkResponse;
});
return cachedResponse || fetchPromise;
})
);
});
„`

Cache versioning ir cleanup

Viena didžiausių problemų su cache – kaip jį atnaujinti kai pasikeitė aplikacija? Jei tiesiog pakeičiate failus serveryje, vartotojai vis tiek matys senus iš cache. Sprendimas – cache versioning.

Pastebėjote `CACHE_NAME` konstantą su `-v1` gale? Tai versijos numeris. Kai deploy’inate naują aplikacijos versiją, pakeičiate į `-v2`, `-v3` ir t.t. Bet tai tik pusė sprendimo – reikia išvalyti senus cache’us:

„`javascript
self.addEventListener(‘activate’, event => {
const cacheWhitelist = [CACHE_NAME];

event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheWhitelist.indexOf(cacheName) === -1) {
console.log(‘Deleting old cache:’, cacheName);
return caches.delete(cacheName);
}
})
);
})
);
});
„`

Activate event’as įvyksta kai naujas Service Worker perima kontrolę. Čia išvalome visus cache’us, kurių nėra whitelist’e. Tai užtikrina, kad seni resursai neužims vietos vartotojo device’e.

Dar vienas svarbus momentas – `self.clients.claim()`. Normaliai naujas Service Worker pradeda kontroliuoti pages tik po refresh’o. Jei norite, kad jis perimtų kontrolę iš karto:

„`javascript
self.addEventListener(‘activate’, event => {
event.waitUntil(
Promise.all([
// Cache cleanup kaip aukščiau
self.clients.claim()
])
);
});
„`

Cache size limitai ir strateginis resursų valdymas

Negalite cache’inti visko be galo. Naršyklės turi limitus – paprastai apie 50MB Chrome’e, bet gali skirtis. Kai viršijate limitą, naršyklė pradeda šalinti senus cache’us. Problema – ji nežino kurie jums svarbūs.

Geriausia praktika – būti selektyviam. Nekiškit į cache kiekvieno API response. Štai kaip galima implementuoti cache size limitą:

„`javascript
const limitCacheSize = (name, size) => {
caches.open(name).then(cache => {
cache.keys().then(keys => {
if(keys.length > size) {
cache.delete(keys[0]).then(() => {
limitCacheSize(name, size);
});
}
});
});
};
„`

Galite kviesti šitą funkciją po kiekvieno cache.put():

„`javascript
cache.put(request, response).then(() => {
limitCacheSize(CACHE_NAME, 50);
});
„`

Taip užtikrinsite, kad cache’e bus ne daugiau kaip 50 įrašų, šalinant seniausius.

Dar vienas approach – skirtingi cache’ai skirtingiems dalykams:

„`javascript
const STATIC_CACHE = ‘static-v1’;
const DYNAMIC_CACHE = ‘dynamic-v1’;
const IMAGE_CACHE = ‘images-v1’;
„`

Statiniams resursams galite turėti vieną cache be limito (jų nedaug ir retai keičiasi), dinaminiams – su limitu, paveikslėliams – atskirą su savo limitu. Taip turite geresnę kontrolę ir galite implementuoti skirtingas cleanup strategijas.

Debugging ir development workflow

Service Workers debugging gali būti pain. Jie cache’ina viską taip gerai, kad kartais nematote savo pakeitimų. Keletas tips’ų:

Chrome DevTools -> Application tab -> Service Workers sekcija. Čia galite:
– Unregister worker’į
– Update on reload (labai naudingas development’e)
– Bypass network for this request
– Matyt visus aktyvius workers

Cache Storage sekcijoje matote visus cache’us ir jų turinį. Galite ištrinti atskirų cache’ų arba atskirų įrašų. Development metu dažnai tiesiog ištrinu visus cache’us ir perkraunu page.

Svarbus dalykas – Service Worker update lifecycle. Kai pakeičiate service-worker.js failą, naujas worker’is neperima kontrolės iš karto. Jis laukia kol visi tab’ai su sena versija bus uždaryti. Development’e tai erzina, todėl naudoju `skipWaiting()`:

„`javascript
self.addEventListener(‘install’, event => {
self.skipWaiting(); // Development only!
// … kitas install kodas
});
„`

Production’e būkite atsargūs su skipWaiting() – gali sukelti issues jei sena page versija bando naudoti naujus cache’intus resursus.

Dar vienas lifesaver – console.log’ai Service Worker’yje matosi Chrome DevTools console, bet reikia filtruoti pagal source. Arba galite naudoti Chrome’s service worker specific console: chrome://serviceworker-internals/

Realūs use case’ai ir ką išmokau praktikoje

Dirbau projekte kur turėjome SPA su dideliu kiekiu paveikslėlių ir API calls. Pradžioje cache’inau viską agresyviai – rezultatas buvo greitas, bet vartotojai matė outdated data. Turėjau rasti balansą.

Sprendimas buvo toks: statiniai assets (JS, CSS) – cache first su versioning. Paveikslėliai – cache first, bet su stale-while-revalidate logika jei paveikslėlis senesnis nei 7 dienos. API calls – network first, bet su 3 sekundžių timeout – jei serveris neatsakė per 3 sek, rodome iš cache ir background’e tęsiame bandymą.

„`javascript
const fetchWithTimeout = (request, timeout = 3000) => {
return Promise.race([
fetch(request),
new Promise((_, reject) =>
setTimeout(() => reject(new Error(‘timeout’)), timeout)
)
]);
};

// API handling su timeout
if (url.pathname.startsWith(‘/api/’)) {
event.respondWith(
fetchWithTimeout(request)
.then(response => {
const clone = response.clone();
caches.open(DYNAMIC_CACHE).then(cache => {
cache.put(request, clone);
});
return response;
})
.catch(() => {
return caches.match(request).then(cached => {
if (cached) return cached;
// Jei nėra cache ir network failed – grąžiname custom error response
return new Response(JSON.stringify({
error: ‘Offline’,
message: ‘No cached data available’
}), {
headers: { ‘Content-Type’: ‘application/json’ }
});
});
})
);
}
„`

Kitas use case – offline page. Kai vartotojas visiškai offline ir bando pasiekti page, kurią neturime cache’e, rodome custom offline page:

„`javascript
// Install metu cache’iname offline page
self.addEventListener(‘install’, event => {
event.waitUntil(
caches.open(STATIC_CACHE).then(cache => {
return cache.addAll([
‘/offline.html’,
‘/styles/offline.css’
]);
})
);
});

// Fetch metu jei viskas fails – rodome offline page
self.addEventListener(‘fetch’, event => {
if (event.request.mode === ‘navigate’) {
event.respondWith(
fetch(event.request)
.catch(() => {
return caches.match(‘/offline.html’);
})
);
}
});
„`

Kai cache tampa jūsų geriausiu draugu (o ne priešu)

Implementavus Service Workers su protingu cache valdymu, mūsų aplikacijos load time sumažėjo nuo ~3 sekundžių iki ~0.5 sekundės repeat visitors. First-time visitors vis tiek mato normalų load, bet returning users gauna beveik instant experience. Lighthouse performance score pakilo nuo 65 iki 95.

Svarbiausia pamoka – nėra vienos teisingos strategijos. Reikia suprasti savo aplikacijos poreikius ir vartotojų elgesį. Jei turite daug static content – agresyviai cache’inkite. Jei real-time data kritinė – būkite atsargesni.

Taip pat neužmirškite monitoring’o. Implementuokite analytics, kad matytumėte cache hit rates, offline usage, error rates. Google Analytics palaiko offline tracking – events saugomi locally ir siunčiami kai vartotojas vėl online.

Service Workers nėra silver bullet, bet tinkamai naudojami gali transformuoti jūsų web app iš „dar vieno website” į kažką kas jaučiasi kaip native aplikacija. Vartotojai gal nesupras techninio aspekto, bet tikrai pajus skirtumą greityje ir patikimume. O tai galiausiai ir yra tikslas, ar ne?

Headless CMS architektūra: Strapi panaudojimas

Kas ta headless architektūra ir kodėl ji tapo tokia populiari

Prisimenu laikus, kai kūrėme svetaines su WordPress ar Drupal, ir viskas buvo sulipdyta į vieną gabalą – tiek backend, tiek frontend. Veikė, žinoma, bet kai reikėdavo tą patį turinį rodyti mobilioje aplikacijoje ar dar kur nors, prasidėdavo tikras galvos skausmas. Headless CMS atsirado kaip atsakas į šią problemą, ir dabar jau sunku įsivaizduoti modernų projektą be šios architektūros.

Esmė paprasta: atskiri turinį nuo jo pateikimo. Backend rūpinasi tik duomenimis ir jų valdymu, o kaip tas turinys bus rodomas – tai jau visiškai kitas klausimas. Per API gauni duomenis ir darai su jais ką nori. Nori React aplikaciją? Prašom. Flutter mobilią? Jokių problemų. Net IoT įrenginiams gali turinį siųsti, jei reikia.

Strapi čia išsiskiria tuo, kad jis open-source, pakankamai lankstus ir turi tikrai gerą developer experience. Nereikia pradėti nuo nulio – gauni paruoštą admin panelę, autentifikaciją, teisių valdymą. Bet kartu nesi užrakintas – gali keisti beveik viską.

Strapi įdiegimas ir pradinė konfigūracija

Pradėti su Strapi tikrai nesudėtinga, bet yra keletas niuansų, kuriuos verta žinoti iš karto. Pirmiausia, reikia normalios Node.js versijos – rekomenduoju naudoti LTS (dabar tai būtų 18.x ar 20.x). Su senesnėmis versijomis gali veikti, bet kam rizikuoti?

Naują projektą sukurti galima viena komanda:

npx create-strapi-app@latest mano-projektas --quickstart

Flag’as --quickstart automatiškai naudoja SQLite duomenų bazę, kas puiku developmentui. Bet production’e tikrai rekomenduoju PostgreSQL arba MySQL. SQLite tiesiog nėra skirtas rimtam darbui su daug concurrent connections.

Kai projektas sukurtas, pirmas dalykas – sukonfigūruoti aplinkos kintamuosius. Strapi naudoja .env failą, ir čia svarbu neužmiršti kelių dalykų:

  • APP_KEYS – šie raktai naudojami session encryption’ui, generuojami automatiškai, bet production’e būtinai turi būti unikalūs
  • API_TOKEN_SALT – svarbu API token’ų saugumui
  • ADMIN_JWT_SECRET – admin panelės JWT autentifikacijai
  • JWT_SECRET – API JWT autentifikacijai

Vienas iš dažniausių klaidų, kurias matau – žmonės palieka default reikšmes production’e. Tai saugumo katastrofa. Naudokite stiprius, atsitiktinius string’us.

Content Types kūrimas ir ryšių valdymas

Čia prasideda tikrasis darbas. Strapi turi puikų Content-Type Builder’į, kuris leidžia sukurti duomenų struktūras per UI. Bet kai projektas auga, greičiau ir patogiau tai daryti per kodą.

Pavyzdžiui, jei kuriate blog’ą, jums reikės Article ir Author content type’ų. Strapi juos saugo src/api/ direktorijoje. Kiekvienas content type turi schema failą, kur aprašote laukus ir jų tipus:

{
"kind": "collectionType",
"collectionName": "articles",
"info": {
"singularName": "article",
"pluralName": "articles",
"displayName": "Article"
},
"attributes": {
"title": {
"type": "string",
"required": true
},
"content": {
"type": "richtext"
},
"author": {
"type": "relation",
"relation": "manyToOne",
"target": "api::author.author",
"inversedBy": "articles"
}
}
}

Ryšiai tarp content type’ų – tai vieta, kur dažnai kyla klausimų. Strapi palaiko visus standartinius ryšių tipus: oneToOne, oneToMany, manyToOne, manyToMany. Svarbu suprasti, kad kai sukuriate ryšį, reikia pagalvoti apie abi puses – jei Article turi Author, tai Author turėtų turėti inversedBy nuorodą atgal į articles.

Praktiškas patarimas: nenaudokite per daug nested relations. Jei turite Article -> Author -> Company -> Country, ir bandote viską fetch’inti vienu query, performance nukentės. Geriau darykite kelis atskirus request’us arba naudokite populate strategiškai.

API customizacija ir lifecycle hooks

Strapi automatiškai generuoja REST ir GraphQL endpoints visiem content type’ams, bet realybėje beveik visada reikia kažką customizuoti. Gal reikia papildomos validacijos, gal norite modifikuoti duomenis prieš išsaugant, gal reikia integracijos su trečiųjų šalių servisais.

Lifecycle hooks – tai vieta, kur dažniausiai vyksta tokia logika. Kiekvienas content type gali turėti savo lifecycle failą src/api/[content-type]/content-types/[content-type]/lifecycles.js:

module.exports = {
async beforeCreate(event) {
const { data } = event.params;
// Pavyzdžiui, automatiškai generuojame slug iš title
if (data.title && !data.slug) {
data.slug = data.title
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/(^-|-$)/g, '');
}
},

async afterCreate(event) {
const { result } = event;
// Galime siųsti notification, invalidate cache, ir t.t.
console.log('Naujas įrašas sukurtas:', result.id);
}
};

Yra keletas hook’ų, kuriuos galite naudoti: beforeCreate, afterCreate, beforeUpdate, afterUpdate, beforeDelete, afterDelete. Kiekvienas gauna event objektą su visa reikalinga informacija.

Kai reikia dar daugiau kontrolės, galite override’inti visus controller’ius ar service’us. Pavyzdžiui, jei norite custom find logikos:

// src/api/article/controllers/article.js
const { createCoreController } = require('@strapi/strapi').factories;

module.exports = createCoreController('api::article.article', ({ strapi }) => ({
async find(ctx) {
// Custom logika prieš standartinį find
const { query } = ctx;

// Galime pridėti papildomus filtrus
query.filters = {
...query.filters,
publishedAt: { $notNull: true }
};

const { data, meta } = await super.find(ctx);

// Galime modifikuoti response
return { data, meta };
}
}));

Autentifikacija ir teisių valdymas

Strapi turi built-in autentifikacijos sistemą, kuri veikia gerai, bet reikia suprasti jos niuansus. Yra du atskiri autentifikacijos kontekstai: admin panelė ir API users. Tai visiškai atskiros sistemos su skirtingais JWT token’ais.

API users valdymui Strapi naudoja Users & Permissions plugin’ą. Čia galite sukurti roles (Authenticated, Public, ir custom), ir kiekvienai role priskirti permissions konkretiems endpoints. Bet default setup’as dažnai per liberalus production’ui.

Štai ką rekomenduoju padaryti iš karto:

  • Public role – leisti tik read operacijas tam turiniui, kuris tikrai public
  • Authenticated role – leisti tik tai, ką user’is tikrai turi teisę daryti
  • Sukurti custom roles specifiniams use case’ams (Editor, Moderator, ir pan.)

Dažna klaida – palikti visus endpoints public, nes „taip lengviau developinti”. Paskui užmiršti pakeisti production’e. Geriau iš karto konfigūruoti teisingai.

Kai reikia labai specifinės teisių logikos (pvz., user gali redaguoti tik savo įrašus), naudokite policies. Sukuriate policy failą:

// src/policies/is-owner.js
module.exports = async (policyContext, config, { strapi }) => {
const { id } = policyContext.params;
const userId = policyContext.state.user.id;

const entity = await strapi.entityService.findOne(
'api::article.article',
id
);

if (entity.author.id !== userId) {
return false;
}

return true;
};

Ir priskirkite jį route’ui:

// src/api/article/routes/article.js
module.exports = {
routes: [
{
method: 'PUT',
path: '/articles/:id',
handler: 'article.update',
config: {
policies: ['is-owner']
}
}
]
};

Media biblioteka ir failų valdymas

Strapi media library yra viena iš stipriausių jo pusių, bet čia irgi yra savo specifika. Default’u failai saugomi local file system’e, kas development’ui puiku, bet production’e tikrai norite naudoti cloud storage.

Populiariausi provider’iai: AWS S3, Cloudinary, DigitalOcean Spaces. Konfigūracija gana paprasta, reikia įdiegti atitinkamą plugin’ą ir sukonfigūruoti credentials:

npm install @strapi/provider-upload-aws-s3

Tada config/plugins.js:

module.exports = ({ env }) => ({
upload: {
config: {
provider: 'aws-s3',
providerOptions: {
accessKeyId: env('AWS_ACCESS_KEY_ID'),
secretAccessKey: env('AWS_ACCESS_SECRET'),
region: env('AWS_REGION'),
params: {
Bucket: env('AWS_BUCKET'),
},
},
},
},
});

Vienas dalykas, kurį dažnai užmiršta – image optimization. Strapi automatiškai generuoja kelis image format’us (thumbnail, small, medium, large), bet jei reikia daugiau kontrolės, galite naudoti sharp library ir custom middleware.

Praktinis patarimas: jei turite daug images, įjunkite lazy loading frontend’e ir naudokite responsive images su srcset. Strapi jums duoda visus reikalingus format’us, tereikia jais pasinaudoti.

Performance optimization ir caching strategijos

Kai projektas pradeda augti, performance tampa kritiška. Strapi pats savaime nėra lėtas, bet jei nekonfigūruojate teisingai, gali tapti. Keletas dalykų, į kuriuos būtina atkreipti dėmesį:

Database queries optimization. Strapi naudoja Knex.js po kapotas, ir jei neatsargiai naudojate populate, galite lengvai gauti N+1 query problemą. Visada naudokite populate strategiškai:

// Blogai
const articles = await strapi.entityService.findMany('api::article.article', {
populate: '*' // Fetch'ina VISKĄ, įskaitant nested relations
});

// Gerai
const articles = await strapi.entityService.findMany('api::article.article', {
populate: {
author: {
fields: ['name', 'email']
},
coverImage: {
fields: ['url', 'formats']
}
}
});

Response caching. Strapi neturi built-in HTTP cache, bet galite lengvai pridėti. Rekomenduoju naudoti Redis su kažkuo kaip strapi-plugin-rest-cache arba implementuoti custom middleware su node-cache.

Paprastas caching middleware pavyzdys:

// config/middlewares.js
module.exports = [
'strapi::errors',
{
name: 'strapi::security',
config: {
contentSecurityPolicy: {
useDefaults: true,
directives: {
'connect-src': ["'self'", 'https:'],
'img-src': ["'self'", 'data:', 'blob:', 'https://your-cdn.com'],
'media-src': ["'self'", 'data:', 'blob:', 'https://your-cdn.com'],
upgradeInsecureRequests: null,
},
},
},
},
'strapi::cors',
'strapi::poweredBy',
'strapi::logger',
'strapi::query',
'strapi::body',
'strapi::session',
'strapi::favicon',
'strapi::public',
];

Database indexing. Jei darote daug queries pagal tam tikrus laukus, pridėkite index’us. Strapi leidžia tai daryti schema definition’e:

"slug": {
"type": "string",
"unique": true,
"index": true
}

Deployment ir production best practices

Kai ateina laikas deploy’inti į production, yra keletas dalykų, kuriuos būtina padaryti teisingai. Pirmiausia – aplinkos kintamieji. Niekada, NIEKADA necommit’inkite .env failo su production credentials. Naudokite environment variables per jūsų hosting platformą.

Database migracijų valdymas – tai kitas svarbus aspektas. Strapi automatiškai susinchronizuoja schema su database development’e, bet production’e rekomenduoju išjungti auto-migration ir valdyti migraciją rankiniu būdu:

// config/database.js
module.exports = ({ env }) => ({
connection: {
client: 'postgres',
connection: {
host: env('DATABASE_HOST'),
port: env.int('DATABASE_PORT'),
database: env('DATABASE_NAME'),
user: env('DATABASE_USERNAME'),
password: env('DATABASE_PASSWORD'),
ssl: env.bool('DATABASE_SSL', false) && {
rejectUnauthorized: env.bool('DATABASE_SSL_REJECT_UNAUTHORIZED', true)
}
},
pool: {
min: 2,
max: 10
}
}
});

Monitoring ir logging – būtinybė production’e. Integruokite Strapi su Sentry error tracking’ui, naudokite Winston ar Pino logging’ui, setup’inkite health check endpoint’us.

Paprastas health check galite padaryti custom route’u:

// src/api/health/routes/health.js
module.exports = {
routes: [
{
method: 'GET',
path: '/health',
handler: 'health.check',
config: {
auth: false
}
}
]
};

// src/api/health/controllers/health.js
module.exports = {
async check(ctx) {
try {
// Patikriname database connection
await strapi.db.connection.raw('SELECT 1');

ctx.body = {
status: 'ok',
timestamp: new Date().toISOString()
};
} catch (error) {
ctx.status = 503;
ctx.body = {
status: 'error',
message: error.message
};
}
}
};

Kai viskas susiglaudžia į vieną paveikslą

Dirbant su Strapi jau keletą metų, galiu pasakyti, kad tai tikrai solid pasirinkimas headless CMS projektams. Taip, yra dalykų, kurie galėtų būti geresni – dokumentacija kartais atsilieka nuo kodo, kai kurie plugin’ai nebepalaikomi, performance tuning’as reikalauja pastangų. Bet bendras developer experience yra tikrai geras.

Svarbiausias dalykas – neskubėti ir iš karto setup’inti projektą teisingai. Užtrunka papildomą dieną-dvi sukonfigūruoti proper authentication, caching, monitoring, bet vėliau sutaupysite savaičių ar net mėnesių. Matau daug projektų, kur žmonės pradeda su quickstart setup’u ir tiesiog tęsia su tuo į production. Paskui stebisi, kodėl viskas lėta ir nestabilu.

Dar vienas patarimas – sekite Strapi community ir GitHub issues. Framework’as aktyviai vystomas, kas mėnesį išeina nauji release’ai. Kartais breaking changes, kartais naujos features. Verta būti kurse, kas vyksta.

Ir galiausiai – Strapi nėra silver bullet. Jei jūsų projektas labai specifinis, su sudėtinga business logika, gal geriau būtų custom backend. Bet jei reikia content management sistemos, kuri lanksti, plečiama ir turi gerą DX – Strapi tikrai verta rimto dėmesio. Tiesiog padarykite namų darbus, suprasite architektūrą, ir turėsite solid foundation savo projektui.

Log failų analizė ir stebėjimas

Kodėl log failai yra IT specialisto geriausias draugas

Kiekvienas, kas dirba su serveriais, aplikacijomis ar bet kokia sudėtingesne sistema, anksčiau ar vėliau susiduria su situacija, kai reikia išsiaiškinti, kas nutiko. Kodėl sistema lūžo 3 val. nakties? Kas užkrovė duomenų bazę? Kodėl vartotojai skundžiasi lėtu atsiliepimo laiku? Atsakymai į šiuos klausimus slypi log failuose – tose begalinėse teksto eilučių srovėse, kurios atrodo kaip Matrix kodas pradedantiesiems.

Log failai yra tarsi juodoji dėžė jūsų sistemai. Jie fiksuoja viską – nuo paprasčiausių informacinių pranešimų iki kritinių klaidų, kurios gali sugriaut visą infrastruktūrą. Problema ta, kad šių failų būna daug, labai daug. Vidutiniu dydžiu aplikacija gali generuoti gigabaitus logų per dieną, o jei kalbame apie mikroservisų architektūrą su dešimtimis ar šimtais servisų – skaičiai tampa tiesiog kosminiški.

Čia ir prasideda tikrasis iššūkis. Neužtenka tiesiog turėti logus – reikia mokėti juos analizuoti, stebėti ir, svarbiausia, iš jų išgauti vertingą informaciją. Tai ne tik reaktyvus procesas, kai ieškome problemų priežasčių po fakto, bet ir proaktyvus – kai stebime tendencijas ir pastebime anomalijas prieš joms virštant rimtomis problemomis.

Ką iš tiesų reiškia geras logavimas

Prieš kalbant apie analizę, verta suprasti, kas sudaro gerą logavimo praktiką. Deja, daugelis projektų šią dalį traktuoja kaip antraeilę – tiesiog išmeta console.log() ar print() sakinius kur papuola ir tikisi, kad tai padės ateityje. Spoileris: nepadės.

Geras logavimas prasideda nuo struktūros. Kiekvienas log įrašas turėtų turėti aiškų lygį (level) – DEBUG, INFO, WARNING, ERROR, CRITICAL. Tai ne tik teorinė klasifikacija, bet praktinis įrankis filtruoti ir prioritizuoti informaciją. Produkcijoje jums tikrai nereikia DEBUG lygio logų, kurie užpildo diskus nereikalinga informacija, bet jums tikrai reikia visų ERROR ir CRITICAL įrašų.

Kontekstas – štai kas daro logą iš tiesų naudingą. Pranešimas „Error occurred” yra visiškai nenaudingas. O štai „Failed to connect to database ‘users_db’ at 192.168.1.50:5432, connection timeout after 30s, user: api_service” – tai jau kažkas. Matote skirtumą? Antruoju atveju turite viską, ko reikia pradėti tyrimą: ką bandėte padaryti, kur, kada ir kokiu kontekstu.

Struktūrizuoti logai JSON formatu tapo de facto standartu šiuolaikinėse sistemose. Vietoj:

[2024-01-15 14:23:45] ERROR: User login failed for user [email protected] from IP 203.0.113.42

Geriau turėti:

{
  "timestamp": "2024-01-15T14:23:45.123Z",
  "level": "ERROR",
  "event": "user_login_failed",
  "user_email": "[email protected]",
  "ip_address": "203.0.113.42",
  "reason": "invalid_password",
  "attempt_number": 3
}

Tokį formatą nepalyginamai lengviau parsinti, indeksuoti ir analizuoti automatizuotomis priemonėmis.

Įrankiai, be kurių neišsiversite

Teorija teorija, bet praktikoje reikia konkrečių įrankių. Log analizės ekosistema yra plati, ir pasirinkimas priklauso nuo jūsų poreikių, biudžeto ir infrastruktūros dydžio.

ELK Stack (Elasticsearch, Logstash, Kibana) – tai klasika, kuri veikia ir veikia gerai. Logstash surenka logus iš įvairių šaltinių, apdoroja ir transformuoja juos, Elasticsearch indeksuoja ir saugo, o Kibana suteikia vizualizacijos sąsają. Šis derinys gali apdoroti milžiniškas log sroves, bet reikia žinoti, kad Elasticsearch gali būti išteklių ėdrus – RAM ir diskų jam niekada nebus per daug.

Praktinis patarimas: jei tik pradedate su ELK, naudokite Filebeat vietoj Logstash pradiniam log surinkimui. Filebeat yra daug lengvesnis ir paprastesnis, o Logstash palikite sudėtingesnėms transformacijoms, jei jų iš tiesų reikia.

Grafana Loki – tai santykinai naujas žaidėjas, kurį sukūrė Grafana Labs. Skirtingai nei Elasticsearch, Loki neindeksuoja log turinio, o tik metaduomenis (labels). Tai reiškia žymiai mažesnius išteklius, bet ir tam tikrus apribojimus paieškos galimybėse. Jei jau naudojate Grafana metrikoms, Loki natūraliai integruojasi į tą pačią ekosistemą.

Graylog – dar viena populiari open-source alternatyva, kuri siūlo gerą balansą tarp funkcionalumo ir paprastumo. Turi įtaisytą MongoDB metaduomenims ir Elasticsearch logams, plius neblogą web sąsają iš dėžės.

Debesų pasaulyje turime specializuotus sprendimus: AWS CloudWatch Logs, Google Cloud Logging, Azure Monitor. Jie puikiai integruojasi su atitinkamomis platformomis, bet gali tapti brangūs, kai log kiekiai auga. Būtinai sukonfigūruokite retention policies ir filtrus, kad nesaugotumėte visko amžinai.

Kaip iš tiesų analizuoti logus efektyviai

Turite įrankius, logai plaukia – kas toliau? Čia prasideda tikrasis darbas. Pirmiausia – niekada, girdite, NIEKADA nebandykite analizuoti logų tiesiog skaitydami juos teksto redaktoriuje. Tai kelias į beprotnamį, ypač kai failai siekia gigabaitus.

Pradėkite nuo agregatų ir statistikos. Užuot žiūrėję į individualius įrašus, pažiūrėkite į bendrus šablonus. Kiek ERROR lygio pranešimų per valandą? Kaip tai keičiasi laike? Kokios yra dažniausios klaidos? Elasticsearch ir Kibana čia puikiai tinka – galite sukurti agregacijas pagal bet kokį lauką ir vizualizuoti rezultatus.

Pavyzdžiui, jei matote, kad 500 klaidos staiga išaugo 10 kartų, tai akivaizdus signalas. Bet jei jos pamažu auga savaitę, galite to nepastebėti žiūrėdami į individualius įrašus.

Paieškos užklausos – išmokite jas gerai. Elasticsearch naudoja Lucene užklausų sintaksę, kuri labai galinga. Pavyzdžiui:

level:ERROR AND service:payment AND timestamp:[now-1h TO now]

Ši užklausa suranda visas klaidas payment servise per paskutinę valandą. Galite pridėti wildcard simbolius, reguliarias išraiškas, range užklausas – galimybės beveik begalinės.

Koreliacijos – tai sudėtingesnė, bet labai vertinga technika. Dažnai problemos priežastis slypi ne viename servise, o keliuose. Pavyzdžiui, API grąžina timeout, nes duomenų bazė lėta, o duomenų bazė lėta, nes cache servisas neveikia. Norint tai pamatyti, reikia koreliuoti logus iš skirtingų šaltinių.

Čia labai padeda correlation ID – unikalus identifikatorius, kuris perduodamas per visą request chain. Kai vartotojas daro užklausą, ji gauna ID, kuris įrašomas į logus kiekviename servise, per kurį praeina. Vėliau galite surasti visus su ta užklausa susijusius logus visose sistemose.

Alertai ir proaktyvus stebėjimas

Analizė post-factum yra naudinga, bet dar geriau – sužinoti apie problemas iš karto, kai jos atsiranda, arba net prieš joms tampant kritinėmis. Čia į žaidimą įeina alerting sistema.

Pagrindinis principas – ne per daug alertų. Jei gaunate 50 email’ų per dieną apie įvairias smulkmenas, greitai pradėsite juos ignoruoti, ir praleisite tikrą problemą. Alert fatigue yra reali problema daugelyje organizacijų.

Sukurkite alertus tik tikrai svarbiems dalykams:
– Kritinės klaidos, kurios tiesiogiai veikia vartotojus
– Neįprasti kiekių pokyčiai (pvz., klaidų skaičius išaugo 5x per 5 minutes)
– Sistemos išteklių problemos (disko vieta baigiasi, memory leaks)
– Security incidentai (nepavykę login bandymai iš neįprastų vietų)

Naudokite threshold-based ir anomaly-based alertus. Pirmieji suveikia, kai kažkas viršija nustatytą ribą (pvz., >100 klaidų per minutę). Antrieji naudoja machine learning aptikti nukrypimus nuo normalaus elgesio – tai gali pagauti problemas, kurių nenumatėte.

Kibana, Grafana ir kiti įrankiai turi įtaisytus alerting mechanizmus. Pavyzdžiui, Elasticsearch Watcher leidžia sukurti sudėtingas taisykles:

{
  "trigger": {
    "schedule": { "interval": "5m" }
  },
  "input": {
    "search": {
      "request": {
        "indices": ["logs-*"],
        "body": {
          "query": {
            "bool": {
              "must": [
                { "match": { "level": "ERROR" }},
                { "range": { "@timestamp": { "gte": "now-5m" }}}
              ]
            }
          }
        }
      }
    }
  },
  "condition": {
    "compare": { "ctx.payload.hits.total": { "gt": 50 }}
  },
  "actions": {
    "send_email": {
      "email": {
        "to": "[email protected]",
        "subject": "High error rate detected",
        "body": "Found {{ctx.payload.hits.total}} errors in last 5 minutes"
      }
    }
  }
}

Šis watcher kas 5 minutes tikrina, ar nėra daugiau nei 50 klaidų, ir jei yra – siunčia email.

Log retention ir valdymo strategijos

Logai užima vietą. Daug vietos. Jei nesukonfigūruosite retention politikų, greitai pamatysite, kad jūsų diskai pilni, o sistemos pradeda lūžti. Tai ne teorinė problema – tai nutinka realybėje, ir dažniau nei galite pagalvoti.

Sukurkite tiered retention strategiją:
– Hot tier (greitas prieigos): paskutinių 7-14 dienų logai, saugomi SSD, pilnai indeksuoti
– Warm tier (vidutinis): 15-90 dienų logai, gali būti lėtesniuose diskuose, sumažinta replication
– Cold tier (archyvas): 90+ dienų logai, compressed, galbūt S3 ar panašioje object storage

Ne visi logai vienodai svarbūs. DEBUG lygio logai iš development aplinkos gali būti ištrinti po savaitės. Production ERROR logai turėtų būti saugomi ilgiau – bent kelis mėnesius, o kartais ir metus (priklausomai nuo compliance reikalavimų).

Elasticsearch Index Lifecycle Management (ILM) leidžia automatizuoti šį procesą. Galite sukurti politiką, kuri automatiškai perkelia senus indeksus į warm tier, vėliau į cold, ir galiausiai ištrina:

PUT _ilm/policy/logs_policy
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": {
            "max_size": "50GB",
            "max_age": "1d"
          }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "shrink": { "number_of_shards": 1 },
          "forcemerge": { "max_num_segments": 1 }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "freeze": {}
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}

Saugumo aspektai ir compliance

Logai dažnai turi jautrią informaciją. Vartotojų IP adresai, email’ai, kartais net autentifikacijos tokenai ar kreditinių kortelių duomenys (nors to tikrai neturėtų būti). GDPR ir kiti privatumo reglamentai reikalauja atsakingo požiūrio į tokius duomenis.

Pirmiausia – niekada neloginkite slaptažodžių, tokenų ar kitų credentials. Tai atrodo akivaizdu, bet vis dar matau projektų, kur tai nutinka. Jei jau atsitiko – turite procesą tokiems logams išvalyti ar ištrinti.

Naudokite log masking ar redaction technikas. Daugelis log processing įrankių leidžia automatiškai aptikti ir užmaskuoti jautrius duomenis. Pavyzdžiui, Logstash gali naudoti grok patterns su mutate filter:

filter {
  mutate {
    gsub => [
      "message", "\d{16}", "****-****-****-****",
      "message", "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}", "***@***.***"
    ]
  }
}

Access control – ne visi turėtų matyti visus logus. Production logai su vartotojų duomenimis turėtų būti prieinami tik tam tikriems žmonėms. Elasticsearch turi role-based access control (RBAC), kurį būtina sukonfigūruoti.

Audit logai – tai meta-logai, kurie fiksuoja, kas prieina prie log sistemų, ką ieško, ką eksportuoja. Jei turite compliance reikalavimus (PCI DSS, HIPAA, SOC2), audit trail yra privalomas.

Realūs scenarijai ir troubleshooting patarimai

Teorija baigėsi, dabar keletas praktinių scenarijų iš realaus gyvenimo.

Scenarijus 1: Lėtas API response

Vartotojai skundžiasi, kad aplikacija lėta. Žiūrite į API logus ir matote, kad response time’ai normalūs. Kur problema? Pradėkite nuo pilno request lifecycle:

1. Patikrinkite load balancer logus – gal problema ten?
2. Pažiūrėkite į aplikacijos logus su timestamp’ais – kiek laiko užtrunka kiekvienas žingsnis?
3. Duomenų bazės logai – gal slow queries?
4. Network latency – gal problema tarp servisų?

Naudokite distributed tracing (Jaeger, Zipkin) kartu su logais. Tai leidžia matyti visą request kelią su timing’ais kiekviename etape.

Scenarijus 2: Intermittent failures

Blogiausias scenarijus – klaida, kuri pasirodo retai ir neprognozuojamai. Čia reikia kantrybės ir metodiškumo:

1. Surinkite VISUS klaidų atvejus per ilgesnį laiką (savaitę ar dvi)
2. Ieškokite šablonų – gal klaida atsiranda tam tikru laiku? Tam tikrais serveriais? Tam tikrais vartotojais?
3. Koreliuokite su kitais įvykiais – deployment’ais, traffic spike’ais, infrastructure changes
4. Pridėkite papildomo logavimo aplink įtartiną kodą

Kartais problema slypi ne kode, o infrastruktūroje – network glitches, disk I/O spikes, memory pressure. Koreliuokite application logus su system metrics.

Scenarijus 3: Security incident

Pastebėjote neįprastą aktyvumą – daug failed login attempts iš skirtingų IP. Kaip reaguoti?

1. Greitai identifikuokite scope – kiek accountų paveikta, iš kur atakuojama
2. Patikrinkite, ar kurie nors bandymai pavyko
3. Pažiūrėkite, ar po sėkmingų login’ų buvo neįprasto aktyvumo
4. Implementuokite rate limiting, jei dar neturite
5. Dokumentuokite viską – incident response reikalauja detalių

Elasticsearch agregacijos čia labai padeda:

GET logs-*/_search
{
  "size": 0,
  "query": {
    "bool": {
      "must": [
        { "match": { "event": "login_failed" }},
        { "range": { "@timestamp": { "gte": "now-1h" }}}
      ]
    }
  },
  "aggs": {
    "by_ip": {
      "terms": { "field": "ip_address", "size": 100 },
      "aggs": {
        "by_user": {
          "terms": { "field": "username", "size": 100 }
        }
      }
    }
  }
}

Kai logai tampa per dideli problemai

Galiausiai, kalbėkime apie tai, kas nutinka, kai jūsų log infrastruktūra išauga iki tokio dydžio, kad pati tampa problema. Tai nutinka dažniau nei galvojate – sėkmingos kompanijos auga, traffic auga, logų auga eksponentiškai.

Sampling – ne visada reikia loginti viską. Jei turite milijonus užklausų per minutę, galbūt užtenka loginti 1% ar 10%? Svarbiausia – loginti visas klaidas ir neįprastus atvejus, bet normalūs success case’ai gali būti samplinami. Daugelis framework’ų palaiko adaptive sampling, kur sample rate priklauso nuo situacijos.

Structured logging libraries – naudokite gerus įrankius. Python’e – structlog, Java – Logback su logstash-logback-encoder, Node.js – winston ar pino. Šie įrankiai padeda išlaikyti konsistenciją ir efektyvumą.

Centralizuotas konfigūravimas – kai turite šimtus servisų, nenorite keisti log level’ų kiekviename atskirai. Naudokite centralizuotą configuration management (Consul, etcd) arba feature flags sistemą, kuri leidžia dinamiškai keisti log settings be redeploy.

Cost optimization – debesų logging servisai gali tapti labai brangūs. Reguliariai peržiūrėkite, ką loginate ir ar tai tikrai reikalinga. Kartais paprasta kodo optimizacija, kuri sumažina verbose logging, gali sutaupyti tūkstančius per mėnesį.

Pagaliau – mokykite komandą. Geriausios logging įrankiai ir praktikos nieko nereiškia, jei developeriai nemoka jų naudoti. Code review turėtų įtraukti logging kokybės vertinimą. Dokumentuokite savo logging standartus ir įsitikinkite, kad visi juos žino.

Log failų analizė ir stebėjimas nėra vienkartinis projektas – tai nuolatinis procesas, kuris evoliucionuoja kartu su jūsų sistema. Pradėkite nuo pagrindų, laipsniškai pridėkite sudėtingesnių dalykų, ir svarbiausia – išmokite skaityti savo sistemą per jos logus. Tai įgūdis, kuris atsipirks šimtus kartų, kai 3 val. nakties reikės išsiaiškinti, kodėl viskas sudužo, ir greitai viską pataisyti.

WebP formato vaizdų įdiegimas svetainėje

Kodėl WebP formatas tapo tokiu populiariu

Prisimenu, kaip prieš keletą metų kūrėjai skeptiškai žiūrėjo į WebP formatą. Google’as jį pristatė dar 2010-aisiais, bet ilgą laiką jis buvo tarsi tas naujas vaikas mokykloje – visi žiūri, bet niekas nenori draugauti. Dabar situacija kardinaliai pasikeitė. Safari pagaliau pridėjo palaikymą 2020-aisiais, ir tai buvo tarsi paskutinis trūkstamas dėlionės gabalas.

WebP formatas gali sumažinti vaizdų dydį 25-35% palyginus su JPEG, o kartais ir daugiau. Tai nėra teorija – tai realūs skaičiai, kuriuos matau kiekvieną dieną dirbdamas su įvairių dydžių projektais. Kai svetainėje turite 50-100 vaizdų, šis skirtumas tampa labai apčiuopiamas. Puslapio įkrovimo laikas sumažėja, Google’as jus myli labiau, o vartotojai neišeina iš svetainės laukdami, kol pagaliau užsikraus tas didžiulis hero vaizdas.

Bet čia ne tik apie dydį. WebP palaiko ir pramatumą (kaip PNG), ir animacijas (kaip GIF), ir net lossy bei lossless kompresijas. Tai tarsi šveicariškas peilis vaizdų pasaulyje.

Kaip techniškai veikia WebP konvertavimas

Pirmiausia reikia suprasti, kad WebP nėra magija. Tai tiesiog efektyvesnis būdas suspausti vaizdų duomenis. Formatas naudoja VP8 vaizdo kodeko technologiją, kuri iš pradžių buvo sukurta video suspaudimui. Štai kodėl jis toks efektyvus – iš esmės kiekvienas WebP vaizdas yra vieno kadro video failas.

Konvertuoti esamus vaizdus į WebP galite keliais būdais. Paprasčiausias – naudoti komandinės eilutės įrankius. Jei dirbate su Linux ar Mac, tikriausiai jau turite įdiegtą ImageMagick:

convert input.jpg -quality 85 output.webp

Arba galite naudoti oficialų Google WebP įrankį:

cwebp -q 85 input.jpg -o output.webp

Kokybės parametras (-q ar -quality) yra kritiškai svarbus. Aš paprastai naudoju 80-85 intervalą. Žemiau 75 pradeda matytis artefaktai, o virš 90 failai tampa per dideli ir prarandate WebP pranašumą.

Bet rankiniu būdu konvertuoti šimtus vaizdų yra košmaras. Todėl automatizavimas tampa būtinas. Jei naudojate Node.js projektą, sharp biblioteka yra puikus pasirinkimas:


const sharp = require('sharp');

sharp('input.jpg')
.webp({ quality: 85 })
.toFile('output.webp');

Implementacija su fallback mechanizmu

Dabar prie smagiausios dalies – kaip faktiškai įdiegti WebP vaizdus svetainėje taip, kad viskas veiktų sklandžiai. Nors šiuolaikiniai naršyklės palaiko WebP, vis dar yra senesnių versijų, kurios nesupranta, kas tai per formatas.

HTML5 picture elementas yra jūsų geriausias draugas šioje situacijoje. Jis leidžia nurodyti kelis vaizdų šaltinius, ir naršyklė automatiškai pasirenka tą, kurį gali atvaizduoti:


<picture>
<source srcset="image.webp" type="image/webp">
<source srcset="image.jpg" type="image/jpeg">
<img src="image.jpg" alt="Aprašymas">
</picture>

Šis metodas veikia puikiai, nes naršyklė pati nusprendžia, kurį formatą naudoti. Jei palaiko WebP – naudos jį, jei ne – nukris į JPEG. Img tagai pabaigoje veikia kaip ultimate fallback senoms naršyklėms, kurios net nepažįsta picture elemento.

Bet štai kur daugelis kūrėjų suklysta – jie pamiršta apie responsive vaizdus. Jūs galite kombinuoti WebP su skirtingų dydžių vaizdais:


<picture>
<source
srcset="image-small.webp 400w, image-medium.webp 800w, image-large.webp 1200w"
type="image/webp">
<source
srcset="image-small.jpg 400w, image-medium.jpg 800w, image-large.jpg 1200w"
type="image/jpeg">
<img src="image-medium.jpg" alt="Aprašymas">
</picture>

CSS background vaizdų optimizavimas

Su HTML vaizdais viskas gana paprasta, bet ką daryti su CSS background vaizdais? Čia reikalingas kiek kitoks požiūris. Negalite tiesiog parašyti background-image: url('image.webp') ir tikėtis, kad viskas veiks visur.

Vienas iš būdų – naudoti modernizr ar panašią biblioteką, kuri prideda klasę prie HTML elemento, jei naršyklė palaiko WebP:


.hero-section {
background-image: url('hero.jpg');
}

.webp .hero-section {
background-image: url('hero.webp');
}

Bet aš paprastai renkuosi JavaScript sprendimą, kuris tikrina WebP palaikymą ir prideda atitinkamą klasę:


function checkWebPSupport() {
const elem = document.createElement('canvas');
if (elem.getContext && elem.getContext('2d')) {
return elem.toDataURL('image/webp').indexOf('data:image/webp') === 0;
}
return false;
}

if (checkWebPSupport()) {
document.documentElement.classList.add('webp');
} else {
document.documentElement.classList.add('no-webp');
}

Šis kodas veikia greitai ir patikimai. Jis sukuria canvas elementą ir bando eksportuoti jį kaip WebP. Jei pavyksta – naršyklė palaiko formatą.

Automatizavimas build proceso metu

Jei rankiniu būdu konvertuojate kiekvieną vaizdą, greitai išprotėsite. Automatizavimas yra vienintelis normalus būdas dirbti su WebP dideliuose projektuose.

Webpack vartotojams yra puikus image-webpack-loader pluginas, kurį galite sukonfigūruoti taip:


{
test: /\.(jpe?g|png)$/i,
use: [
'file-loader',
{
loader: 'image-webpack-loader',
options: {
webp: {
quality: 85
}
}
}
]
}

Gulp fanams rekomenduoju gulp-webp:


const gulp = require('gulp');
const webp = require('gulp-webp');

gulp.task('images', () =>
gulp.src('src/images/*.{jpg,png}')
.pipe(webp({ quality: 85 }))
.pipe(gulp.dest('dist/images'))
);

Bet mano asmeninis favoritas yra Next.js Image komponentas, kuris automatiškai konvertuoja vaizdus į WebP (ar net AVIF, jei naršyklė palaiko) be jokių papildomų konfigūracijų:


import Image from 'next/image';

<Image
src="/images/photo.jpg"
width={800}
height={600}
alt="Aprašymas"
/>

Next.js automatiškai sugeneruos WebP versiją, optimizuos dydį pagal ekraną, ir net lazy-loadins pridės. Tai vienas iš retų atvejų, kai framework’as tikrai palengvina gyvenimą.

CDN ir serverio konfigūracija

Vien turėti WebP failus nepakanka – reikia, kad serveris juos teisingai atsiųstų. Daugelis kūrėjų pamiršta nustatyti teisingus MIME tipus, ir tada naršyklės nesuprata, ką daryti su gautais failais.

Apache serveryje pridėkite į .htaccess:


AddType image/webp .webp

Nginx konfigūracijoje:


types {
image/webp webp;
}

Dar geriau – galite sukonfigūruoti serverį automatiškai siųsti WebP versiją, jei naršyklė ją palaiko. Nginx pavyzdys:


location ~* ^/images/.+\.(jpe?g|png)$ {
set $webp_suffix "";
if ($http_accept ~* "image/webp") {
set $webp_suffix ".webp";
}

add_header Vary Accept;
try_files $uri$webp_suffix $uri =404;
}

Šis konfigūracija tikrina, ar naršyklė priima WebP (žiūri į Accept headerį), ir jei taip – bando rasti .webp versiją. Jei neranda – grąžina originalų failą.

CDN lygmenyje situacija dar paprastesnė. Cloudflare, CloudFront ir kiti pagrindiniai CDN provideriai turi integruotą WebP palaikymą. Cloudflare Polish funkcija net automatiškai konvertuoja vaizdus į WebP, jei įjungsite. Bet aš paprastai vengu tokių automatinių sprendimų – geriau kontroliuoti procesą pačiam.

Performance metrikų stebėjimas

Įdiegus WebP, svarbu pamatuoti, ar tai tikrai davė rezultatų. Teorija yra gražu, bet praktika kartais nustebina.

Google PageSpeed Insights yra akivaizdus pasirinkimas, bet aš rekomenduoju žiūrėti į realius vartotojų duomenis per Chrome User Experience Report. Jis rodo, kaip jūsų svetainė veikia tikrų žmonių naršyklėse, ne tik laboratorinėse sąlygose.

WebPageTest.org leidžia testuoti iš skirtingų lokacijų su skirtingomis naršyklėmis. Paleidžiu testą su WebP ir be jo, ir lyginu rezultatus. Paprastai matau 20-30% pageweight sumažėjimą, o tai tiesiogiai atspindi įkrovimo laike.

Lighthouse Chrome DevTools yra kasdieninis įrankis. Jis ne tik parodo, kiek sutaupėte, bet ir nurodo, kuriuos vaizdus dar galima optimizuoti. Kartais pamirštu konvertuoti kokį nors vaizdą, ir Lighthouse man tai primena.

Realaus pasaulio pavyzdys: neseniai optimizavau e-commerce svetainę, kurioje buvo apie 80 produktų nuotraukų. Po WebP įdiegimo:
– Bendras puslapio dydis sumažėjo nuo 4.2MB iki 2.8MB
– First Contentful Paint pagerėjo nuo 2.1s iki 1.4s
– Largest Contentful Paint nukrito nuo 3.8s iki 2.6s

Tai nėra mažas skirtumas. Vartotojai tikrai pajuto, kad svetainė tapo greitesnė.

Paplitusios klaidos ir kaip jų išvengti

Per metus dirbant su WebP įdiegimais įvairiuose projektuose, mačiau tas pačias klaidas kartojantis vėl ir vėl.

Klaida #1: Per agresyvi kompresija

Kai kurie kūrėjai nustato quality į 60 ar net žemiau, manydami, kad mažesnis failas visada geresnis. Bet kai vaizdas atrodo kaip supikselizuotas košmaras, niekas nesidžiaugs greitesniu įkrovimu. Laikykitės 80-85 intervalo daugumoje atvejų. Fotografijoms galite nuleisti iki 75, bet ne žemiau.

Klaida #2: Pamiršti alt tekstus

Kai perdarote HTML į picture elementus, lengva pamiršti alt atributą. Bet jis vis dar būtinas prieinamumui ir SEO. Visada įtraukite prasmingą alt tekstą į img tagą.

Klaida #3: Neišlaikyti originalų failą

Kartą sutikau kūrėją, kuris ištrynė visus originalius JPEG failus po konvertavimo į WebP. Vėliau paaiškėjo, kad reikia kitokios kokybės versijos, ir teko ieškoti backup’ų. Visada laikykite originalus – diskų vieta pigi, o laiko atkūrimui nėra.

Klaida #4: Ignoruoti lazy loading

WebP sumažina failų dydį, bet jei įkeliate 50 vaizdų iš karto, vis tiek bus lėta. Kombinuokite su lazy loading:


<img src="image.webp" loading="lazy" alt="Aprašymas">

Moderniose naršyklėse loading=”lazy” atributas veikia native, be jokių JavaScript bibliotekų.

Klaida #5: Netestuoti senose naršyklėse

Taip, WebP palaiko visos šiuolaikinės naršyklės, bet vis dar yra vartotojų su IE11 ar senesnėmis Android naršyklėmis. Visada testuokite fallback mechanizmą. BrowserStack ar panašios paslaugos čia labai praverčia.

Ateities perspektyvos ir alternatyvos

WebP yra puikus, bet technologijos nestovi vietoje. AVIF formatas jau beldžiasi į duris ir žada dar geresnę kompresiją – kartais 50% mažesnius failus nei WebP. Chrome ir Firefox jau palaiko, Safari pridėjo palaikymą iOS 16 ir macOS Ventura.

Bet ar turėtumėte skubėti pereiti prie AVIF? Dar ne. Palaikymas vis dar nepakankamai platus, o kodavimo laikas gerokai ilgesnis nei WebP. Aš rekomenduoju progressive enhancement požiūrį:


<picture>
<source srcset="image.avif" type="image/avif">
<source srcset="image.webp" type="image/webp">
<source srcset="image.jpg" type="image/jpeg">
<img src="image.jpg" alt="Aprašymas">
</picture>

Taip naršyklės, kurios palaiko AVIF, gaus pačią efektyviausią versiją, kitos gaus WebP, o seniausios – JPEG. Bet tai prideda papildomos kompleksiškumo ir reikalauja generuoti tris skirtingus failus.

Mano nuomone, 2024-2025 metais WebP vis dar yra sweet spot – pakankamai platus palaikymas, gera kompresija, brandūs įrankiai. AVIF stebėkite, eksperimentuokite, bet neskubėkite daryti jį pagrindiniu formatu.

Dar viena įdomi alternatyva – JPEG XL. Jis žadėjo būti next-gen formatas, bet Chrome komanda nusprendė nutraukti palaikymą 2022-aisiais, nors vėliau vėl svarstė grąžinti. Situacija neaiški, todėl kol kas geriau laikytis WebP ir stebėti AVIF raidą.

Praktinis patarimas: jei kuriate naują projektą dabar, įdiekite WebP su galimybe lengvai pridėti AVIF vėliau. Naudokite picture elementą ir automatizuotus build procesus, kurie leidžia lengvai pridėti naujus formatus be kodo keitimo. Taip būsite pasiruošę ateičiai, bet nenukentės dabartinė funkcionalumas.

Galiausiai, neužmirškite, kad vaizdų optimizavimas – tai ne vienkartinis darbas. Nauji vaizdai pridedami nuolat, todėl automatizavimas ir aiškūs workflow’ai yra kritiškai svarbūs. Sukurkite sistemą, kuri automatiškai konvertuoja naujus vaizdus, ir jūsų svetainė išliks greita ilgą laiką.

Sanity.io kaip content management sprendimas

Kas tas Sanity ir kodėl apie jį visi kalba

Jei dirbi su web projektais, tikrai esi girdėjęs apie headless CMS sistemą Sanity.io. O jei dar ne – tikrai verta susipažinti, nes šis įrankis pastaraisiais metais tapo vienu iš populiariausių content management sprendimų tarp kūrėjų. Bet kas gi jį daro tokį ypatingą?

Sanity.io – tai ne tiesiog dar viena CMS platforma. Tai pilnai pritaikomas, API-first content management sprendimas, kuris leidžia kurti bet kokio tipo turinį ir jį naudoti bet kur – svetainėse, mobiliose aplikacijose, IoT įrenginiuose ar net skaitmeniniuose ekranuose. Jie patys save vadina „platform for structured content”, ir tai tikrai tinkamas apibūdinimas.

Įkurta 2017 metais Norvegijoje, Sanity greitai įgavo populiarumą tarp kūrėjų dėl savo lankstumo ir developer-friendly požiūrio. Skirtingai nei tradicinės CMS sistemos, Sanity neriboja tavo technologijų pasirinkimo – gali naudoti React, Vue, Svelte, ar bet kurį kitą framework’ą. Turinys saugomas kaip struktūrizuoti duomenys ir pasiekiamas per API, o tai suteikia neįtikėtiną laisvę.

Architektūra ir techninis pagrindas

Sanity architektūra paremta keliais pagrindiniais komponentais. Pirmiausia – tai Sanity Studio, open-source React aplikacija, kurią gali visiškai pritaikyti pagal savo poreikius. Tai redagavimo aplinka, kurią diegiesi kaip dalį savo projekto arba host’ini atskirai.

Antra dalis – Content Lake. Tai Sanity tvarkomą duomenų bazė, kurioje saugomas visas tavo turinys. Duomenys čia saugomi kaip JSON dokumentai ir yra prieinami realiu laiku per GROQ (Graph-Relational Object Queries) arba GraphQL API. Content Lake automatiškai tvarko versijų kontrolę, leidžia grįžti prie senesnių versijų ir užtikrina duomenų saugumą.

Trečias komponentas – API sluoksnis. Sanity suteikia labai greitą CDN-powered API, kuris leidžia gauti duomenis iš bet kurio įrenginio ar platformos. Jie teigia, kad jų API atsakymo laikas yra apie 50-100ms globaliai, kas yra tikrai įspūdinga.

Kas įdomu – Sanity Studio yra tiesiog React aplikacija, kurią gali kustomizuoti kaip nori. Gali pridėti custom input komponentus, sukurti savo dashboard’us, integruoti trečiųjų šalių servisus. Tai ne black box, o pilnai kontroliuojamas įrankis.

Realtime kolaboracija ir content modeliavimas

Viena iš Sanity killer features – tai realtime kolaboracija. Kai keli žmonės redaguoja tą patį dokumentą, matai jų kursorus ir pakeitimus realiu laiku, panašiai kaip Google Docs. Tai gali skambėti kaip smulkmena, bet kai dirbi su didesne komanda, šis funkcionalumas tampa neįkainojamas.

Content modeliavimas Sanity vyksta per schemas, kurias apibrėžia JavaScript/TypeScript kodu. Pavyzdžiui, jei nori sukurti blog post schema:


export default {
name: 'post',
type: 'document',
title: 'Blog Post',
fields: [
{
name: 'title',
type: 'string',
title: 'Title'
},
{
name: 'slug',
type: 'slug',
title: 'Slug',
options: {
source: 'title'
}
},
{
name: 'content',
type: 'array',
title: 'Content',
of: [{type: 'block'}]
}
]
}

Šis code-first požiūris gali atrodyti keistas tiems, kas įpratę prie tradicinių CMS sistemų su GUI, bet jis suteikia milžinišką lankstumą. Schemas gali versijuoti su Git, lengvai perkelti tarp projektų, automatizuoti su skriptais.

Sanity palaiko įvairius field tipus – nuo paprastų string ir number iki sudėtingų reference, array ir object tipų. Gali kurti savo custom field tipus, pridėti validacijas, conditional fields. Sistema leidžia sukurti tikrai sudėtingas content struktūras be jokių apribojimų.

GROQ – užklausų kalba, kuri tikrai veikia

Vienas iš dalykų, kuris išskiria Sanity iš kitų headless CMS – tai jų sukurta užklausų kalba GROQ (Graph-Relational Object Queries). Iš pradžių gali atrodyti kaip dar viena kalba, kurią reikia išmokti, bet tikėk manim – ji verta dėmesio.

GROQ sintaksė yra intuityvesnė nei GraphQL daugeliui use case’ų. Pavyzdžiui, jei nori gauti visus publikuotus blog įrašus su autoriaus informacija:


*[_type == "post" && published == true] {
title,
slug,
publishedAt,
author->{
name,
image
}
}

Matai kaip skaitoma? Filtruoji dokumentus pagal type ir published statusą, tada projektuoji reikiamus laukus. Arrow sintaksė -> leidžia „išskleisti” references ir gauti susijusius duomenis vienu užklausimu.

GROQ palaiko filtering, sorting, projections, joins, aggregations – viską, ko reikia sudėtingoms užklausoms. Ir kas svarbiausia – ji veikia greitai. Sanity optimizavo GROQ engine’ą taip, kad net sudėtingos užklausos grąžintų rezultatus per šimtadalius sekundės.

Tiesa, jei labiau mėgsti GraphQL – Sanity palaiko ir jį. Gali automatiškai sugeneruoti GraphQL schema iš savo content modelio ir naudoti standartines GraphQL užklausas. Bet aš rekomenduočiau bent išbandyti GROQ – ji tikrai užauga ant tavęs.

Portable Text ir rich content valdymas

Kai kalba pasisuka apie rich text content’ą, dauguma CMS sistemų naudoja HTML arba Markdown. Sanity pasirinko kitą kelią – jie sukūrė Portable Text specifikaciją. Tai JSON-based formatas, kuris aprašo struktūrizuotą tekstą kaip duomenų objektų masyvą.

Kodėl tai svarbu? Nes HTML yra presentation format, ne data format. Kai saugai turinį kaip HTML, tu iš esmės įkalini jį į tam tikrą atvaizdavimo būdą. Portable Text leidžia saugoti turinį kaip struktūrizuotus duomenis ir spręsti kaip jį atvaizduoti kiekviename kanale atskirai.

Pavyzdžiui, tas pats Portable Text content gali būti renderinamas kaip HTML svetainėje, kaip natyvūs UI komponentai mobilėje aplikacijoje, arba net konvertuojamas į speech sintezatoriui. Tai tikras omnichannel content management.

Sanity Studio’je Portable Text editorius yra labai galingas. Palaiko įprastus formatavimo įrankius, custom annotations, inline objektus, embedded content. Gali įterpti bet kokius custom komponentus – galerijas, video playerius, code snippets, ką tik nori.

Ir kas geriausia – Portable Text yra atvira specifikacija. Yra oficialūs serializers React, Vue, PHP, Python ir kitoms platformoms. Community sukūrė dar daugiau įrankių. Tai reiškia, kad nesi užrakintas Sanity ekosistemoje.

Asset management ir image pipeline

Sanity asset management sistema nusipelno atskiro skyriaus. Jie turi integruotą CDN ir image processing pipeline, kuris automatiškai optimizuoja ir transformuoja vaizdus pagal poreikius.

Kai uploadini vaizdą į Sanity, jis automatiškai saugomas jų CDN ir tampa prieinamas per globalų tinklą. Bet čia prasideda įdomesnė dalis – gali transformuoti vaizdus on-the-fly naudojant URL parametrus. Reikia thumbnail? Pridedi ?w=200&h=200&fit=crop. Reikia WebP formato? ?fm=webp. Automatic quality optimization? ?auto=format.

Tai reiškia, kad nebereikia kurti kelių vaizdų versijų ir jų saugoti. Vienas source failas, o Sanity CDN sugeneruoja ir cache’ina reikiamus variantus pagal užklausą. Tai sutaupo daug storage vietos ir palengvina content valdymą.

Sanity taip pat palaiko hotspot/crop funkcionalumą. Content editoriai gali nurodyti svarbią vaizdo dalį (pavyzdžiui, veido lokaciją portrete), ir kai vaizdas cropinamas skirtingiems aspect ratio, sistema automatiškai užtikrina, kad svarbi dalis liktų matoma.

Video ir kitų media failų valdymas taip pat yra integruotas. Gali saugoti failus tiesiogiai Sanity arba integruoti su Cloudinary, Mux ar kitais specialized media servisais.

Deployment, pricing ir praktiniai aspektai

Sanity Studio deployment’as yra paprastas. Kadangi tai tiesiog React aplikacija, gali ją deploy’inti bet kur – Vercel, Netlify, AWS, ar net savo serveryje. Oficialiai Sanity rekomenduoja sanity deploy komandą, kuri automatiškai deploy’ina Studio į jų infrastruktūrą su custom subdomain’u.

Dėl pricing modelio – Sanity turi nemokamą tier’ą, kuris yra gana generous. Gauni 3 vartotojus, 10GB bandwidth, 500k API užklausų per mėnesį. Tai daugiau nei pakanka mažesniems projektams ar development’ui. Mokamas planas prasideda nuo $99/mėn ir suteikia daugiau resources bei advanced features.

Kas svarbu žinoti – Sanity skaičiuoja bandwidth’ą ir API užklausas. Jei tavo projektas turi didelį traffic’ą, gali greitai viršyti limitus. Čia svarbu optimizuoti užklausas ir naudoti caching strategijas. Next.js su ISR (Incremental Static Regeneration) arba panašūs sprendimai gali labai sumažinti API calls skaičių.

Kalbant apie performance – Sanity API yra tikrai greitas. Jų CDN turi edge locations visame pasaulyje, todėl latency yra minimalus. Bet kaip ir su bet kuria API-based sistema, reikia galvoti apie caching. Sanity palaiko ETags ir conditional requests, kas leidžia efektyviai cache’inti duomenis.

Integracijos ir ekosistema

Viena iš Sanity stiprybių – plati ekosistema ir integracijos. Oficialiai palaikomi starter’iai su Next.js, Gatsby, Nuxt, Eleventy ir kitais populiariais framework’ais. Yra plugins preview funkcionalumui, internationalization, SEO, analytics.

Sanity turi aktyvią community, kuri kuria įvairius plugins ir įrankius. Slack channel’as yra gyvas, dokumentacija išsami, o support komanda responsive. Tai svarbu, nes kai užstrigi su problema, nori greitai gauti pagalbą.

Integracijos su trečiųjų šalių servisais yra paprastos. Gali prijungti Algolia search, Shopify e-commerce, Stripe payments, Mailchimp email marketing – ką tik nori. Webhooks leidžia trigger’inti automatizacijas kai content pasikeičia.

Kas įdomu – Sanity Studio gali būti embedded’intas į kitas aplikacijas. Pavyzdžiui, gali turėti custom admin panel’ę ir integruoti Sanity content editing kaip jos dalį. Arba sukurti specialized editing experience konkretiems use case’ams.

Kada Sanity yra tinkamas pasirinkimas ir kada ne

Sanity puikiai tinka projektams, kuriems reikia lankstumo ir skalabilumo. Jei kuri headless aplikaciją, omnichannel platformą, ar bet ką, kur turinys turi būti prieinamas per API – Sanity yra puikus pasirinkimas. Ypač gerai veikia su modern JavaScript framework’ais kaip Next.js ar Nuxt.

Jis taip pat puikus, kai dirbi su sudėtingomis content struktūromis. Galimybė apibrėžti schemas kodu ir kurti custom field tipus suteikia beveik neribotą lankstumą. Realtime kolaboracija ir version control yra dideli privalumai komandiniame darbe.

Bet Sanity nėra visada geriausias pasirinkimas. Jei kuri paprastą WordPress-style blog’ą ar corporate website’ą, tradicinė CMS gali būti paprastesnė ir pigesnė. Sanity reikalauja developer expertise – tai ne no-code sprendimas.

Taip pat reikia atsižvelgti į pricing. Jei projektas turi labai didelį traffic’ą, API costs gali išaugti. Čia svarbu gerai suplanuoti caching strategiją ir optimizuoti užklausas. Static site generation su periodic rebuilds gali būti ekonomiškesnis variantas nei pure client-side fetching.

Dar vienas aspektas – vendor lock-in. Nors Sanity duomenys yra JSON formatu ir gali būti eksportuojami, migracija į kitą sistemą nėra trivialu. GROQ užklausos, Portable Text struktūra, custom schemas – visa tai yra Sanity-specific. Reikia gerai pagalvoti prieš commitment’ą.

Bet jei tavo projektas atitinka Sanity sweet spot – modern tech stack, sudėtingas content model, poreikis lankstumui – tai tikrai vienas geriausių sprendimų rinkoje. Developer experience yra puikus, sistema stabili ir greita, o galimybės beveik neribotų.

Galiausiai, Sanity.io yra brandus ir aktyviai vystomas produktas su stipria komanda ir community. Jie nuolat prideda naujas features, pagerina performance, klauso feedback’o. Tai ne kažkoks startup eksperimentas, o rimtas enterprise-ready sprendimas, kurį naudoja tokios kompanijos kaip Figma, Cloudflare, Sonos.

Taigi jei ieškote headless CMS savo kitam projektui ir dar neišbandėte Sanity – tikrai rekomenduoju pasižiūrėti. Pradėti galite su free tier’u, o jų dokumentacija ir starter projektai padės greitai įsibėgėti. Kas žino, galbūt tai bus būtent tas įrankis, kurio jums trūko.

Gatsby static site generatoriaus panaudojimas

Kodėl Gatsby vis dar aktualus 2024-aisiais

Kai prieš kelerius metus pirmą kartą išgirdau apie Gatsby, atrodė, kad tai dar vienas React framework’as, kuris greitai dings iš radaro. Bet štai – jis ne tik neišnyko, bet ir toliau lieka vienu iš populiariausių static site generatorių. Taip, žinau, dabar visi kalba apie Next.js, Astro ir kitus naujokus, bet Gatsby turi savo nišą ir ją užpildo puikiai.

Gatsby iš esmės yra React-based framework’as, kuris generuoja statinius HTML failus. Skamba paprasta, bet po gaubtu slypi GraphQL duomenų sluoksnis, galingas plugin’ų ekosistema ir optimizacijos, kurias rankiniu būdu įgyvendinti užtruktų amžinybę. Jei kada nors kūrėte portfolio, blog’ą ar dokumentacijos puslapį, tikrai susidūrėte su dilema: WordPress? Custom sprendimas? SPA? Gatsby čia siūlo kompromisą tarp greičio, kūrėjo patirties ir galutinio rezultato.

Kas man patinka labiausiai – tai kad Gatsby neprievartauja. Galite pradėti nuo paprasto starter’io ir palaipsniui pridėti sudėtingumo. O kai reikia SEO, greičio ar prieinamumo – visa tai jau įkepinta į framework’ą.

Kaip Gatsby veikia po gaubtu

Pirmą kartą paleidus gatsby develop, gali atrodyti, kad vyksta kažkas panašaus į Next.js. Bet skirtumas fundamentalus. Gatsby build metu iš tikrųjų generuoja visus galimus HTML failus iš anksto. Tai reiškia, kad jūsų serveris (ar CDN) tiesiog atiduoda gatavus failus – jokio server-side rendering’o, jokių duomenų bazių užklausų.

Štai kaip tai veikia praktiškai: jūs rašote React komponentus, apibrėžiate duomenų šaltinius (Markdown failai, CMS, API), o Gatsby build proceso metu:

  • Surenka visus duomenis per GraphQL
  • Paleidžia React komponentus su tais duomenimis
  • Sugeneruoja statinius HTML failus kiekvienam route’ui
  • Optimizuoja paveikslėlius, CSS, JavaScript
  • Sukuria prefetch strategijas greičiau navigacijai

Rezultatas? Pirmas puslapio įkėlimas greitesnis nei daugelio SPA, o vėliau navigacija tarp puslapių vyksta beveik akimirksniu, nes Gatsby po gaubtu naudoja React Router ir prefetch’ina kitus puslapius.

Vienas dalykas, kuris man asmeniškai kėlė klausimų pradžioje – kodėl GraphQL? Atrodo kaip overkill’as paprastam blog’ui. Bet kai pradedi dirbti su keliais duomenų šaltiniais (pvz., Markdown failai + Contentful + REST API), GraphQL sluoksnis tampa išganymu. Visi duomenys prieinami per vieną vientisą API, su type safety ir autocomplete.

Praktinis projekto setup’as nuo nulio

Gerai, užtenka teorijos. Sukurkime realų projektą. Tarkime, norime padaryti tech blog’ą su straipsniais Markdown formatu ir dinamiškai generuojamais puslapiais.

Pradedame standartiškai:

npm init gatsby

CLI paklaus kelių klausimų – pasirinkite TypeScript (jei esate normalus žmogus), styling sprendimą (aš dažniausiai einu su styled-components arba CSS modules), ir ar norite CMS integracijos. Pradžiai galite praleisti CMS – pridėsime vėliau jei reikės.

Po kelių minučių turėsite veikiantį Gatsby projektą. Struktūra bus maždaug tokia:


src/
pages/ # Automatiškai tampa route'ais
components/ # React komponentai
templates/ # Template'ai dinaminiams puslapiams
gatsby-config.js # Pagrindinis config'as
gatsby-node.js # Build-time logika

Dabar įdomi dalis – pridėkime galimybę skaityti Markdown failus. Tam reikės dviejų plugin’ų:

npm install gatsby-source-filesystem gatsby-transformer-remark

gatsby-config.js faile sukonfiguruojame:


plugins: [
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'posts',
path: `${__dirname}/content/posts`,
},
},
'gatsby-transformer-remark',
]

Dabar sukurkite content/posts/ direktoriją ir įmeskite kelis Markdown failus su frontmatter:


---
title: "Mano pirmas Gatsby straipsnis"
date: "2024-01-15"
slug: "pirmas-straipsnis"
---

Čia eina turinys...

GraphQL užklausos ir duomenų gavimas

Vienas iš Gatsby privalumų – GraphiQL playground, prieinamas http://localhost:8000/___graphql. Čia galite eksperimentuoti su užklausomis realiu laiku.

Pavyzdžiui, norint gauti visus blog post’us:


query {
allMarkdownRemark(sort: {frontmatter: {date: DESC}}) {
nodes {
frontmatter {
title
date
slug
}
excerpt
}
}
}

Šią užklausą galite naudoti tiesiog React komponente su useStaticQuery hook’u arba per page query. Skirtumas – useStaticQuery negali priimti kintamųjų, tinka tik statinėms užklausoms.

Praktinis patarimas: pradžioje GraphQL sintaksė gali atrodyti keista, ypač jei ateinat iš REST pasaulio. Bet pasitikėkite – po savaitės jau rašysite užklausas neatidarę dokumentacijos. O autocomplete IDE tikrai padeda.

Dinaminis puslapių generavimas

Gerai, turime Markdown failus, GraphQL užklausas veikia. Bet kaip sukurti atskirą puslapį kiekvienam straipsniui? Čia ateina gatsby-node.js.

Šis failas leidžia hook’intis į Gatsby build procesą. Naudosime createPages API:


exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions

const result = await graphql(`
query {
allMarkdownRemark {
nodes {
frontmatter {
slug
}
}
}
}
`)

result.data.allMarkdownRemark.nodes.forEach(node => {
createPage({
path: `/blog/${node.frontmatter.slug}`,
component: path.resolve('./src/templates/blog-post.js'),
context: {
slug: node.frontmatter.slug,
},
})
})
}

Dabar sukurkite src/templates/blog-post.js template’ą:


export const query = graphql`
query($slug: String!) {
markdownRemark(frontmatter: {slug: {eq: $slug}}) {
html
frontmatter {
title
date
}
}
}
`

const BlogPost = ({ data }) => {
const post = data.markdownRemark
return (

{post.frontmatter.title}

)
}

Atkreipkite dėmesį – GraphQL užklausoje naudojame $slug kintamąjį, kuris ateina iš context objecto createPage funkcijoje.

Paveikslėlių optimizacija ir gatsby-plugin-image

Jei dirbate su bet kokiu content-heavy projektu, paveikslėliai bus jūsų didžiausia problema. Gatsby turi puikų sprendimą – gatsby-plugin-image.

Šis plugin’as automatiškai:

  • Generuoja skirtingų dydžių versijas
  • Konvertuoja į WebP/AVIF formatą
  • Prideda lazy loading
  • Sukuria blur-up placeholder’ius
  • Optimizuoja pagal viewport dydį

Naudojimas paprastas:


import { StaticImage } from 'gatsby-plugin-image'


Jei paveikslėliai ateina iš GraphQL (pvz., iš Markdown frontmatter), naudokite GatsbyImage komponentą su dynamic image data.

Vienas dalykas, kuris gali suklaidinti – StaticImage reikalauja, kad src būtų statinis string’as, ne kintamasis. Tai dėl to, kad Gatsby build metu turi žinoti, kuriuos paveikslėlius optimizuoti. Jei reikia dinamiškumo, naudokite gatsby-source-filesystem su GraphQL.

Plugin’ų ekosistema ir dažniausios integracijos

Gatsby stiprybė – plugin’ai. Yra jų šimtai, ir daugelis išsprendžia problemas, su kuriomis susidurtumėte bet kuriame projekte.

Štai mano asmeninis „must-have” sąrašas:

gatsby-plugin-manifest – generuoja web app manifest’ą, prideda favicon’us, leidžia padaryti PWA. Setup’as trivialus, rezultatas – profesionaliau atrodantis projektas.

gatsby-plugin-sitemap – automatiškai generuoja sitemap.xml. Jei rūpi SEO (o turėtų), tai no-brainer.

gatsby-plugin-google-gtag – Google Analytics integracija. Yra ir kitų analytics sprendimų, bet šis veikia iš dėžės.

gatsby-plugin-feed – RSS feed’as. Taip, RSS dar gyvas, ir jei darote blog’ą, žmonės tikrai naudoja.

gatsby-source-contentful (arba Sanity, Strapi, kt.) – jei dirbate su headless CMS. Gatsby puikiai integruojasi su visais pagrindiniais CMS sprendimais.

Vienas patarimas dėl plugin’ų – neskubėkite pridėti visko iš karto. Pradėkite su minimaliu setup’u ir pridėkite funkcionalumą pagal poreikį. Kiekvienas plugin’as prideda build time, o kai projektas auga, tai tampa jaučiama.

Performance optimizacijos ir deployment

Gatsby iš dėžės duoda gerą performance, bet yra keletas dalykų, kuriuos galite padaryti dar geriau.

Pirma – code splitting. Gatsby tai daro automatiškai, bet galite padėti su dynamic import’ais:


const HeavyComponent = React.lazy(() => import('./HeavyComponent'))

Antra – prefetching strategija. Gatsby default’u prefetch’ina visus link’us, kurie matomi viewport’e. Tai super greita navigacija, bet jei turite šimtus link’ų puslapyje, gali būti per daug. Galite kontroliuoti per gatsby-config.js:


module.exports = {
flags: {
FAST_DEV: true,
PRESERVE_FILE_DOWNLOAD_CACHE: true,
},
}

Trečia – incremental builds. Jei naudojate Gatsby Cloud arba Netlify, galite enable’inti incremental builds, kurie rebuild’ina tik pasikeitusias dalis. Tai gali sumažinti build laiką nuo 10 minučių iki 30 sekundžių.

Dėl deployment’o – Gatsby puikiai veikia su:

  • Netlify (mano asmeninis favoritas, turi puikią Gatsby integraciją)
  • Vercel (taip pat puiku, nors labiau orientuota į Next.js)
  • Gatsby Cloud (oficialus sprendimas, bet mokamas)
  • GitHub Pages (nemokamas, bet ribotesnis)
  • AWS S3 + CloudFront (jei norite full control)

Netlify setup’as paprastas kaip du kartus du – sujunkite GitHub repo, nurodykite build komandą (gatsby build), ir publish direktoriją (public/). Viskas. Kiekvienas push į main branch’ą automatiškai deploy’ins naują versiją.

Ką daryti kai projektas auga ir tampa lėtas

Štai kur Gatsby kartais gauna kritikos – dideli projektai gali turėti ilgus build laikus. Jei turite tūkstančius puslapių, build’as gali trukti 15-30 minučių ar net ilgiau.

Keletas strategijų kaip su tuo kovoti:

Deferred Static Generation (DSG) – naujesnis Gatsby feature’as, leidžiantis atidėti kai kurių puslapių generavimą iki pirmo request’o. Puikiai tinka puslapiams, kurie retai lankami:


export async function config() {
return {
defer: true
}
}

Partial Hydration – ne visi komponentai turi būti interactive. Galite naudoti gatsby-plugin-no-javascript tam tikroms dalims, jei jos tik statinės.

Build cache’inimas – įsitikinkite, kad CI/CD sistema cache’ina .cache ir public direktorijas tarp build’ų.

Duomenų šaltinių optimizacija – jei traukiate duomenis iš external API, įsitikinkite, kad naudojate pagination, filtravimą, ir traukiate tik tai ko reikia.

Vienas realus pavyzdys iš praktikos – dirbau projekte su ~5000 blog post’ų. Build laikas buvo 25 minutės. Po optimizacijų (DSG, geresnės GraphQL užklausos, incremental builds) nukrito iki 3 minučių normaliam update’ui ir ~10 minučių full rebuild’ui.

Ar Gatsby vis dar verta mokytis dabar

Tiesą sakant, Gatsby ekosistema šiek tiek sulėtėjo pastaraisiais metais. Next.js paėmė daug dėmesio, Astro atėjo su naujomis idėjomis, Remix irgi žaidžia toje pačioje aikštelėje. Bet Gatsby vis dar turi savo vietą.

Jei darote content-heavy projektą, kur SEO yra kritinis, ir nereikia daug server-side logikos – Gatsby puikus pasirinkimas. Plugin’ų ekosistema, paveikslėlių optimizacija, ir developer experience vis dar top tier. Dokumentacija išsami, community aktyvus (nors ne toks kaip Next.js), ir yra daugybė realių production projektų ant Gatsby.

Kita vertus, jei darote aplikaciją su daug dinamikos, authentication, real-time features – galbūt Next.js ar Remix būtų geresnis pasirinkimas. Gatsby stipriausia statinio content’o srityje.

Mano asmeninė nuomonė – Gatsby vis dar verta mokytis, ypač jei jau mokate React. Daugelis konceptų (component architecture, GraphQL, build-time optimization) persikeliami į kitus framework’us. O jei kada nors reikės padaryti greitą, SEO-friendly website’ą – Gatsby leis tai padaryti per savaitgalį, ne mėnesį.

Taigi, ar verta? Priklauso nuo use case’o. Bet jei skaitote šį straipsnį, tikėtina kad jūsų use case’as Gatsby teritorijoje. Tad drąsiai eksperimentuokite, pradėkite nuo mažo projekto, ir pamatysite ar jums prie širdies. Worst case – išmoksite React ir GraphQL geriau. Best case – turėsite naują įrankį toolbox’e, kuris išspręs realias problemas.

CloudCannon Git-based CMS Jekyll ir Hugo

Kas yra CloudCannon ir kam jis skirtas

Jei dirbate su statiniais svetainių generatoriais, turbūt jau girdėjote apie CloudCannon. Tai Git-pagrįsta turinio valdymo sistema (CMS), kuri puikiai integruojasi su Jekyll ir Hugo – dviem populiariausiomis statinių svetainių generavimo platformomis. Skirtingai nei tradicinės CMS, tokios kaip WordPress ar Drupal, CloudCannon leidžia išlaikyti visus statinių svetainių privalumus, tuo pačiu suteikdama patogią sąsają turinio redagavimui.

Pagrindinis CloudCannon pranašumas – tai tiltas tarp kūrėjų ir turinio kūrėjų. Kūrėjai gali toliau dirbti su savo mėgstamomis technologijomis, naudoti Git workflow’ą, o turinio autoriai gauna intuityvią vizualinę sąsają, kur gali redaguoti tekstus, keisti nuotraukas ir valdyti svetainės turinį be jokių techninių žinių. Nereikia mokytis Markdown sintaksės ar suprasti, kaip veikia Git komandos.

Sistema veikia tiesiogiai su jūsų Git repozitorija – GitHub, GitLab ar Bitbucket. Kai redaktorius atlieka pakeitimus per CloudCannon sąsają, sistema automatiškai sukuria commit’ą ir atnaujina jūsų repozitoriją. Tai reiškia, kad viskas lieka versijuojama, galite grįžti prie ankstesnių versijų, o deployment’as vyksta automatiškai.

Jekyll integracija: kaip tai veikia praktikoje

Jekyll yra vienas seniausių ir populiariausių statinių svetainių generatorių, ypač mėgstamas GitHub Pages naudotojų. CloudCannon su Jekyll dirba itin sklandžiai, nes platforma iš pradžių buvo kuriama būtent šiam generatoriui palaikyti.

Kai prijungiate Jekyll projektą prie CloudCannon, sistema automatiškai atpažįsta jūsų projekto struktūrą. Ji identifikuoja collections, data failus, layouts ir kitus Jekyll komponentus. Tai reiškia, kad jums nereikia nieko specialiai konfigūruoti – sistema pati supranta, kaip jūsų svetainė sukonstruota.

Vienas įdomiausių dalykų – vizualus redagavimas. CloudCannon leidžia redaguoti turinį tiesiogiai naršyklėje, matant realų rezultatą. Jei turite tinkamai sukonfigūruotus editable regions, turinio kūrėjai gali tiesiog spragtelėti ant teksto ir jį keisti, tarsi dirbtų su WordPress ar kita tradicine CMS. Tai ypač patogu klientams, kurie nenori mokytis techninių dalykų.

Front matter redagavimas taip pat yra labai intuityvus. Vietoj to, kad redaktorius turėtų rašyti YAML sintaksę rankomis, CloudCannon generuoja formas su atitinkamais laukais. Galite sukonfigūruoti, kokie laukai turėtų būti rodomi, kokio tipo jie yra (tekstas, data, pasirinkimas iš sąrašo), net pridėti validacijas. Pavyzdžiui, jei turite blog post’ą su kategorijomis, galite sukurti dropdown meniu su galimomis kategorijomis, užuot leidę redaktoriui rašyti jas rankomis.

Hugo palaikymas ir jo ypatumai

Hugo pastaraisiais metais tapo itin populiarus dėl savo greičio – tai greičiausias statinis generatorius rinkoje. CloudCannon pradėjo palaikyti Hugo vėliau nei Jekyll, bet dabar integracija yra labai brandi ir funkcionaliai turtinga.

Vienas iš Hugo privalumų – jo lankstumas ir galimybė dirbti su dideliais projektais. Jei turite svetainę su tūkstančiais puslapių, Hugo sugeneruos ją per sekundes, o ne minutes. CloudCannon šį pranašumą išlaiko – preview’ai generuojami greitai, o deployment’as vyksta žaibiškai.

Hugo naudoja šiek tiek kitokią struktūrą nei Jekyll. Vietoj collections, Hugo turi content types ir sections. CloudCannon puikiai su tuo susidoroja ir automatiškai atpažįsta jūsų Hugo projekto struktūrą. Archetypes, shortcodes, taxonomies – visa tai palaikoma ir gali būti integruota į redagavimo sąsają.

Vienas dalykas, kurį pastebėjau dirbdamas su Hugo CloudCannon platformoje – tai shortcode’ų palaikymas. Hugo shortcode’ai yra galingas įrankis, leidžiantis įterpti sudėtingą funkcionalumą į turinį. CloudCannon leidžia sukurti snippet’us, kurie atitinka jūsų shortcode’us, ir redaktoriai gali juos įterpti per vizualinę sąsają, užpildydami paprastas formas. Tai labai patogu, kai turite, pavyzdžiui, sudėtingą galerijos shortcode’ą su daugybe parametrų.

Konfigūravimas ir pradžios žingsniai

Pradėti naudoti CloudCannon nėra sudėtinga, bet yra keletas dalykų, kuriuos verta žinoti iš anksto. Pirmiausia, jums reikia turėti veikiančią Jekyll arba Hugo svetainę Git repozitorijoje. Jei dar neturite, galite pradėti nuo vieno iš CloudCannon template’ų – jie turi gana gerą pasirinkimą.

Prijungus repozitoriją, CloudCannon automatiškai bandys build’inti jūsų svetainę. Jei naudojate standartinę konfigūraciją, viskas turėtų veikti iš karto. Bet jei turite specifinių reikalavimų – custom plugins Jekyll atveju arba specifinę Hugo versiją – turėsite tai nurodyti konfigūracijos faile.

CloudCannon konfigūracija vykdoma per cloudcannon.config.yml failą projekto šakniniame kataloge. Čia galite apibrėžti, kaip turėtų atrodyti redagavimo sąsaja, kokie laukai turėtų būti rodomi, kaip turėtų veikti navigacija ir daug kitų dalykų. Pradžioje gali atrodyti, kad reikia daug konfigūruoti, bet iš tikrųjų daugelis dalykų veikia automatiškai, o konfigūracija reikalinga tik specifiniams poreikiams.

Vienas svarbus dalykas – inputs konfigūracija. Tai leidžia apibrėžti, kaip turėtų atrodyti front matter laukai redagavimo sąsajoje. Pavyzdžiui, jei turite datą, galite nurodyti, kad tai turėtų būti date picker. Jei turite nuorodą į kitą puslapį, galite sukonfigūruoti, kad tai būtų dropdown su visais galimais puslapiais. Tai labai pagerina redaktorių patirtį.

Vizualinis redagavimas ir jo galimybės

Viena iš stipriausių CloudCannon pusių – vizualinis redagavimas. Tai ne tik WYSIWYG editorius tekstui – tai galimybė redaguoti turinį tiesiogiai svetainės kontekste, matant, kaip viskas atrodys galutinėje svetainėje.

Kad tai veiktų, jums reikia apibrėžti editable regions savo template’uose. Jekyll atveju tai daroma naudojant specialius class’us arba data atributus. Hugo atveju principas panašus. Kai redaktorius atidaro puslapį vizualiniame režime, jis gali spragtelėti ant bet kurio editable regiono ir pradėti redaguoti.

Vizualinis editorius palaiko įvairius turinio tipus. Galite redaguoti paprastą tekstą, antraštes, sąrašus, nuorodas. Galite įterpti nuotraukas, kurios automatiškai įkeliamos į jūsų asset management sistemą. Galite net pridėti naujus komponentus, jei turite sukonfigūravę structures – tai leidžia apibrėžti pasikartojančias turinio struktūras, kurias redaktoriai gali pridėti ar pašalinti.

Pavyzdžiui, jei turite landing page su skirtingomis sekcijomis (hero, features, testimonials, CTA), galite sukonfigūruoti kiekvieną sekciją kaip atskirą structure. Redaktorius gali pridėti naują features sekciją, užpildyti jos laukus, perkelti ją į kitą vietą – viskas vyksta vizualioje sąsajoje, be jokio kodo rašymo.

Asset management ir optimizavimas

Dirbant su bet kokia svetaine, vienas didžiausių iššūkių – asset’ų valdymas. Nuotraukos, dokumentai, video failai – visa tai reikia kažkur laikyti ir efektyviai valdyti. CloudCannon turi integruotą asset management sistemą, kuri šį procesą daro gana paprastą.

Kai įkeliate nuotrauką per CloudCannon sąsają, ji automatiškai patalpinama į jūsų projekto assets katalogą ir commit’inama į Git. Tai reiškia, kad visi asset’ai yra versijuojami kartu su kodu ir turiniu. Kai kuriems tai gali atrodyti kaip trūkumas (Git repozitorijos dydis auga), bet praktikoje tai suteikia gerą kontrolę ir paprastumą.

CloudCannon taip pat siūlo automatinį nuotraukų optimizavimą. Galite sukonfigūruoti, kad visos įkeliamos nuotraukos būtų automatiškai sumažinamos, konvertuojamos į WebP formatą, generuojamos skirtingų dydžių versijos responsive dizainui. Tai labai patogu, nes redaktoriams nereikia rūpintis technine puse – jie tiesiog įkelia nuotrauką, o sistema pasirūpina optimizavimu.

Vienas įdomus feature’as – DAM (Digital Asset Management) integracija. Jei naudojate išorinę DAM sistemą, tokią kaip Cloudinary ar imgix, galite ją integruoti su CloudCannon. Tai leidžia laikyti asset’us atskirai nuo Git repozitorijos, bet vis tiek valdyti juos per CloudCannon sąsają.

Deployment ir hosting galimybės

CloudCannon ne tik CMS, bet ir hosting platforma. Kai build’as užbaigiamas, svetainė automatiškai deploy’inama į CloudCannon infrastruktūrą. Tai global CDN, todėl jūsų svetainė bus greita visame pasaulyje.

Bet jei norite naudoti kitą hosting’ą, tai taip pat įmanoma. CloudCannon palaiko deployment’ą į AWS S3, Netlify, Vercel, GitHub Pages ir kitas platformas. Galite sukonfigūruoti, kad po kiekvieno sėkmingo build’o svetainė būtų automatiškai deploy’inama į jūsų pasirinktą platformą.

Vienas dalykas, kurį vertinu CloudCannon – tai preview deployment’ai. Kai redaktorius daro pakeitimus, jis gali pamatyti preview prieš publikuojant. Tai atskirtas environment’as, kur galite patikrinti, kaip viskas atrodo, prieš commit’indami pakeitimus į production branch’ą. Tai labai patogu didesniuose projektuose, kur turite approval workflow’ą.

CloudCannon taip pat palaiko branch’ų valdymą. Galite turėti skirtingus branch’us skirtingiems tikslams – development, staging, production. Kiekvienas branch’as gali turėti savo preview URL, ir galite lengvai switch’intis tarp jų. Tai leidžia implementuoti sudėtingus workflow’us, kur pakeitimai pereina per kelis approval stage’us prieš patenkant į production.

Komandinis darbas ir workflow valdymas

Didesniuose projektuose svarbu turėti gerą komandinio darbo organizavimą. CloudCannon siūlo įvairius įrankius tam palengvinti. Pirmiausia – vaidmenų valdymas. Galite apibrėžti skirtingus vaidmenis su skirtingomis teisėmis: administratoriai, redaktoriai, peržiūrėtojai. Kiekvienas vaidmuo gali turėti prieigą tik prie tam tikrų funkcijų.

Pavyzdžiui, redaktoriai gali redaguoti turinį, bet negali keisti svetainės konfigūracijos ar template’ų. Peržiūrėtojai gali tik žiūrėti turinį, bet negali jo keisti. Tai labai svarbu, kai dirbate su klientais ar turite didelę komandą su skirtingais atsakomybės lygiais.

CloudCannon taip pat palaiko approval workflow’us. Galite sukonfigūruoti, kad pakeitimai turėtų būti patvirtinti prieš publikuojant. Kai redaktorius padaro pakeitimus, jie patenka į review stage’ą, ir tik po patvirtinimo yra commit’inami į production branch’ą. Tai ypač naudinga reguliuojamose industrijose ar kai turite griežtus content quality reikalavimus.

Activity log’as leidžia sekti, kas, kada ir ką pakeitė. Tai naudinga audit’ui ir problemų sprendimui. Jei kas nors sugenda, galite greitai pamatyti, kas darė pakeitimus ir grįžti prie ankstesnės versijos. Kadangi viskas vyksta per Git, turite pilną istoriją su commit message’ais.

Ką reikia žinoti prieš pradedant ir kaip išspausti maksimumą

CloudCannon yra galingas įrankis, bet kaip ir bet kuri platforma, jis turi savo ypatumų ir apribojimų. Pirmiausia – kaina. Nemokamas planas yra gana ribotas ir tinka tik mažiems projektams ar testavimui. Rimtiems projektams reikės mokamo plano, o jei turite didelę komandą ar daug svetainių, kaina gali būti gana aukšta.

Antra – learning curve. Nors CloudCannon stengiasi būti intuityvus, pilnai išnaudoti visas galimybes reikia laiko. Ypač konfigūracija gali būti sudėtinga pradžioje. Verta skirti laiko dokumentacijai ir eksperimentavimui su test projektu prieš pradedant dirbti su production svetaine.

Trečia – Jekyll plugins apribojimai. Jei naudojate custom Jekyll plugins, kurie nėra whitelist’e, turėsite problemas. CloudCannon palaiko tik safe plugins, panašiai kaip GitHub Pages. Jei jūsų svetainė remiasi custom plugins, gali tekti juos przepisyti arba ieškoti alternatyvų. Hugo šiuo atžvilgiu yra lankstesnis, nes viskas yra built-in.

Dar vienas patarimas – investuokite laiko į gerą content modeling’ą. Gerai apgalvotas content model’is palengvins redaktoriams darbą ir sumažins klaidų tikimybę. Naudokite structures sudėtingesnėms sekcijoms, apibrėžkite aiškius input types, pridėkite help text’us. Tai gali atrodyti kaip papildomas darbas pradžioje, bet atsipirks vėliau.

Dėl performance – nors CloudCannon hosting’as yra greitas, jei turite labai didelę svetainę ar didelį traffic’ą, galbūt verta apsvarstyti custom hosting sprendimą. CloudCannon gali deploy’inti į bet kurią platformą, todėl galite naudoti AWS CloudFront ar Cloudflare su custom konfigūracija, jei reikia specifinių optimizacijų.

Galiausiai – backup strategija. Nors Git pats savaime yra versijų kontrolės sistema, verta turėti papildomus backup’us. CloudCannon leidžia eksportuoti visą svetainę, tad periodiškai darykite backup’us į atskirą vietą. Taip pat įsitikinkite, kad jūsų Git repozitorija yra saugiai laikoma ir turite prieigą prie jos nepriklausomai nuo CloudCannon.

Sistema tikrai verta dėmesio, jei ieškote būdo sujungti statinių svetainių privalumus su patogia turinio valdymo sąsaja. Ji geriausiai tinka agentūroms, kurios kuria svetaines klientams ir nori suteikti jiems paprastą redagavimo įrankį, išlaikant techninę kontrolę. Taip pat puikiai tinka vidutinio dydžio įmonėms, kurios nori greitų, saugių svetainių be tradicinių CMS komplikacijų. Tiesiog būkite pasirengę skirti laiko pradinei konfigūracijai ir mokymui – rezultatas bus vertas pastangų.

„Amazon SES” e-pašto siuntimo paslauga

Kas ta Amazon SES ir kodėl ji turėtų rūpėti

Jei kada nors bandėte siųsti didelius kiekius el. laiškų tiesiogiai iš savo serverio, turbūt pastebėjote, kad tai ne taip paprasta kaip atrodo. Jūsų laiškai keliauja tiesiai į spam aplanką, IP adresai patenka į juoduosius sąrašus, o pristatymo rodikliai primena loteriją. Štai čia ir ateina į pagalbą Amazon Simple Email Service (SES) – AWS ekosistemos dalis, skirta profesionaliam el. pašto siuntimui.

Amazon SES yra debesų pagrindo el. pašto siuntimo platforma, kuri leidžia siųsti transakcines žinutes, marketingo kampanijas ir bet kokio kito tipo el. laiškus be galvos skausmo dėl infrastruktūros valdymo. Paslauga veikia nuo 2011 metų ir per tą laiką tapo vienu iš populiariausių sprendimų tarp kūrėjų.

Kas ją išskiria? Visų pirma – kaina. Mokate tik už tai, ką naudojate, be jokių mėnesinių mokesčių ar minimumų. Pirmi 62,000 laiškų per mėnesį yra visiškai nemokami, jei siunčiate iš EC2 instancijos. Po to kaina siekia vos $0.10 už 1,000 laiškų. Palyginkite su kitais sprendimais rinkoje – skirtumas akivaizdus.

Kaip pradėti: nuo smėlio dėžės iki gamybos

Pradedant su SES, pirmiausia susiduriate su sandbox režimu. Tai AWS saugumo mechanizmas, kuris neleidžia spamerinėms veikloms. Sandbox aplinkoje galite siųsti tik į patvirtintus el. pašto adresus ir turite 200 laiškų per dieną limitą. Skamba ribojančiai, bet tai protinga apsauga.

Norint išeiti iš sandbox, reikia pateikti prašymą AWS palaikymo komandai. Čia svarbu būti konkrečiam: paaiškinkite, kokio tipo laiškus siųsite, kaip tvarkote atsisakymus (unsubscribe), kaip valdote skundus. AWS tikrai skaito šiuos prašymus, todėl neverta rašyti šabloniškų atsakymų. Paprastai sprendimas priimamas per 24 valandas, nors kartais gali užtrukti ilgiau.

Kai jau išeinate iš sandbox, vis tiek turite „warm-up” procesą. Negalite iš karto pradėti siųsti milijonų laiškų – tai pakels raudonas vėliavėles tiek AWS sistemose, tiek pas el. pašto teikėjus. Pradėkite nuo kelių tūkstančių per dieną ir palaipsniui didinkite apimtis. Stebėkite bounce ir complaint rates – jei jie viršija atitinkamai 5% ir 0.1%, jūsų paskyra gali būti sustabdyta.

Domeno ir el. pašto adresų autentifikavimas

Čia prasideda tikrasis darbas. Norint, kad jūsų laiškai pasiektų gavėjų inbox, o ne spam aplanką, privalote tinkamai sukonfigūruoti domeno autentifikaciją. SES palaiko tris pagrindinius standartus: SPF, DKIM ir DMARC.

DKIM (DomainKeys Identified Mail) yra svarbiausias iš jų. SES automatiškai sugeneruoja DKIM rakto porą, o jums reikia tik pridėti CNAME įrašus į savo DNS zoną. Tai užtikrina, kad jūsų laiškai yra autentiški ir nebuvo pakeisti kelyje. AWS konsolėje rasite tikslias reikšmes – tiesiog nukopijuokite jas į savo DNS valdymo sąsają.

SPF (Sender Policy Framework) įrašas nurodo, kurie serveriai gali siųsti laiškus jūsų domeno vardu. SES dokumentacijoje rasite rekomenduojamą SPF įrašą, kuris paprastai atrodo maždaug taip: `v=spf1 include:amazonses.com ~all`. Jei jau turite SPF įrašą, tiesiog pridėkite amazonses.com include direktyvą.

DMARC politika nusako, kaip el. pašto teikėjai turėtų elgtis su laiškais, kurie nepraėjo SPF ar DKIM patikrinimų. Pradedantiesiems rekomenduoju nustatyti `p=none` su raportavimo adresu – taip galėsite stebėti, kas vyksta, neprarasdami laiškų. Vėliau galite sugriežtinti iki `p=quarantine` ar net `p=reject`.

Integracija su aplikacija: SMTP vs API

SES siūlo du pagrindinius būdus siųsti laiškus: SMTP sąsają ir API. Kiekvienas turi savo privalumų, priklausomai nuo jūsų situacijos.

SMTP metodas puikiai tinka, kai turite esamą aplikaciją, kuri jau naudoja SMTP protokolą. Daugelis CMS sistemų, el. pašto klientų ir programinės įrangos palaiko SMTP iš dėžės. Tiesiog pakeičiate SMTP serverio nustatymus į SES endpoint’ą, pridedate kredencialus (ne savo AWS root kredencialus, dieve mano, bet specialiai sugeneruotus SMTP kredencialus), ir viskas veikia. Endpoint’ai skiriasi pagal regioną, pavyzdžiui: `email-smtp.eu-west-1.amazonaws.com`.

API metodas suteikia daugiau kontrolės ir funkcionalumo. Galite naudoti AWS SDK beveik bet kuriai programavimo kalbai – Python boto3, JavaScript AWS SDK, PHP SDK ir t.t. API leidžia lengviau valdyti šablonus, konfigūruoti configuration sets, gauti detalesnius atsakymus apie siuntimo būseną.

Štai paprastas Python pavyzdys:

„`python
import boto3

ses = boto3.client(‘ses’, region_name=’eu-west-1′)

response = ses.send_email(
Source=’[email protected]’,
Destination={‘ToAddresses’: [‘[email protected]’]},
Message={
‘Subject’: {‘Data’: ‘Testas’, ‘Charset’: ‘UTF-8’},
‘Body’: {‘Text’: {‘Data’: ‘Turinys’, ‘Charset’: ‘UTF-8’}}
}
)
„`

Asmeniškai rekomenduoju API metodą naujiems projektams – jis lankstesnis ir leidžia išnaudoti visas SES galimybes.

Configuration Sets ir siuntimo analizė

Configuration Sets yra viena iš galingiausių SES funkcijų, kurią daugelis nepakankamai išnaudoja. Tai leidžia grupuoti siuntimus, stebėti metrikas ir nukreipti įvykius į kitus AWS servisus.

Sukūrus configuration set, galite pridėti event destinations – kur SES siųs informaciją apie įvykius. Pavyzdžiui, galite nukreipti visus bounce, complaint, delivery, send, reject, open ir click įvykius į CloudWatch, Kinesis Firehose ar SNS. Tai neįkainojama informacija optimizuojant el. pašto kampanijas.

CloudWatch metrikose galite stebėti:
– Delivery rate – kiek laiškų sėkmingai pristatyta
– Bounce rate – kiek grįžo atgal (hard vs soft bounces)
– Complaint rate – kiek gavėjų pažymėjo kaip spam
– Open rate ir click rate (jei įjungtas tracking)

Praktinis patarimas: sukurkite atskirą configuration set kiekvienam laiškų tipui (transakcinis, marketingas, pranešimai). Taip galėsite tiksliau analizuoti, kurie laiškai veikia geriau, ir greitai identifikuoti problemas.

Bounce ir complaint valdymas yra kritiškai svarbus. SES automatiškai tvarko hard bounces – jei el. pašto adresas neegzistuoja, jis nebus bandomas siųsti dar kartą. Tačiau jūs turite įgyvendinti logiką, kuri pašalina tokius adresus iš savo duomenų bazės. Ignoravimas gali baigtis paskyros sustabdymu.

Šablonai ir personalizavimas

SES palaiko el. laiškų šablonus, kurie leidžia atskirti turinį nuo logikos. Galite sukurti šabloną su kintamaisiais, o siųsdami tiesiog perduoti reikšmes. Tai ypač patogu transakciniams laiškams – registracijos patvirtinimas, slaptažodžio atstatymas, sąskaitų siuntimas.

Šablonai kuriami JSON formatu ir gali turėti tiek HTML, tiek text versijas. Štai supaprastintas pavyzdys:

„`json
{
„TemplateName”: „Pasveikinimas”,
„SubjectPart”: „Sveiki, {{vardas}}!”,
„HtmlPart”: „

Labas, {{vardas}}

Ačiū už registraciją {{data}}.

„,
„TextPart”: „Labas, {{vardas}}\n\nAčiū už registraciją {{data}}.”
}
„`

Siųsdami laišką su šablonu, tiesiog nurodote šablono pavadinimą ir perduodate kintamųjų reikšmes. SES automatiškai sugeneruoja galutinį laišką.

Dėl personalizavimo – būkite atsargūs su dinaminiu turiniu. Jei kiekvienas laiškas yra unikalus, negalėsite pasinaudoti batch siuntimo efektyvumu. Kartais geriau siųsti šiek tiek mažiau personalizuotą laišką, bet greitai ir patikimai, nei laukti valandų, kol sugeneruojami tūkstančiai unikalių variantų.

Kainodara ir optimizavimas

Jau minėjau, kad SES yra pigus, bet verta pasigilinti į detales. Bazinė kaina – $0.10 už 1,000 laiškų. Tačiau yra papildomų išlaidų:
– Priedai: $0.12 už GB
– Dedicated IP adresai: $24.95 per mėnesį už IP
– Gaunami laiškai: $0.10 už 1,000 laiškų

Dedicated IP adresai verta investuoti tik jei siunčiate didelius kiekius (bent 50,000+ per dieną) ir norite kontroliuoti savo reputaciją. Mažesnėms apimtims shared IP pool’as veikia puikiai – AWS palaiko gerą reputaciją, o jūs nemokamai naudojatės tuo.

Jei siunčiate labai didelius kiekius, apsvarstykite Kinesis Firehose naudojimą event’ams vietoj CloudWatch – tai gali būti pigiau. Taip pat, jei jums nereikia open ir click tracking, išjunkite juos – tai sumažina duomenų kiekį ir šiek tiek pagerina pristatymo greitį.

Dar vienas optimizavimo būdas – naudoti bulk siuntimo operacijas. SendBulkTemplatedEmail API leidžia siųsti iki 50 laiškų vienu užklausos, kas sumažina API iškvietimų skaičių ir šiek tiek pagreitina procesą.

Dažniausios problemos ir jų sprendimai

Per metus darbo su SES, susidūriau su įvairiomis problemomis. Štai dažniausios ir kaip jas spręsti.

**”Throttling” klaidos** – SES turi siuntimo limitus, kurie priklauso nuo jūsų paskyros istorijos. Pradžioje galite siųsti tik kelis laiškus per sekundę. Sprendimas: įgyvendinkite retry logiką su exponential backoff. AWS SDK dažniausiai tai daro automatiškai, bet jei naudojate SMTP, reikia patiems pasirūpinti.

**Aukštas bounce rate** – jei viršijate 5%, AWS gali sustabdyti siuntimą. Priežastys: prasta el. pašto adresų kokybė, pirkti sąrašai, seni duomenys. Sprendimas: įgyvendinkite double opt-in, reguliariai valykite sąrašus, naudokite el. pašto validacijos servisus prieš pridedant adresus į DB.

**Laiškai patenka į spam** – net su tinkamu DKIM/SPF. Priežastys: prastas turinys, per daug nuorodų, spam trigger žodžiai, žemas engagement. Sprendimas: testuokite laiškus su Mail Tester ar panašiais įrankiais, vengkite agresyvaus marketingo kalbos, įtraukite aiškų unsubscribe mechanizmą.

**Lėtas pristatymas** – laiškai pasiekia gavėjus po kelių minučių ar valandų. Priežastys: gavėjo serverio greylisting, jūsų siuntimo greitis per mažas, regionas per toli. Sprendimas: pasirinkite SES regioną arčiau jūsų auditorijos, padidinkite siuntimo greitį (jei leidžia limitai), naudokite dedicated IP su gera reputacija.

**Complaint rate per aukštas** – žmonės žymi jūsų laiškus kaip spam. Priežastys: siunčiate tiems, kas neprašė, per dažnai siunčiate, turinys neatitinka lūkesčių. Sprendimas: gerbkite savo prenumeratorius, leiskite lengvai atsisakyti, siųskite tik tai, ko žmonės tikisi.

Kai viskas veikia ir ką daryti toliau

Kai jūsų SES infrastruktūra veikia sklandžiai, atėjo laikas galvoti apie tolesnius žingsnius. Pirmiausia – monitoring ir alerting. Sukonfigūruokite CloudWatch alarms bounce ir complaint rate metrikoms. Jei rodikliai viršija saugius ribas, turėtumėte gauti pranešimą iš karto, o ne sužinoti iš AWS email’o, kad jūsų paskyra sustabdyta.

Apsvarstykite feedback loop’ų integraciją. Kai kas nors pažymi jūsų laišką kaip spam, SES gali siųsti notification. Automatiškai pašalinkite tokius vartotojus iš siuntimo sąrašų – tai apsaugos jūsų reputaciją ir sutaupys pinigų.

Jei jūsų verslas auga, verta pagalvoti apie multi-region setup’ą. SES veikia regionuose nepriklausomai, todėl galite turėti failover mechanizmą. Jei viename regione iškyla problemų, automatiškai perjunkite į kitą. Tai ypač svarbu, jei el. paštas yra kritinė jūsų verslo dalis.

Testavimas turėtų būti nuolatinis procesas. Reguliariai siųskite test laiškus į skirtingus el. pašto teikėjus (Gmail, Outlook, Yahoo, Apple Mail) ir tikrinkite, kaip jie atrodo, ar nepatenka į spam. El. pašto teikėjai nuolat keičia savo algoritmus, todėl tai, kas veikė prieš mėnesį, gali nebeveikti dabar.

Dokumentuokite savo setup’ą. Kai po metų reikės pakeisti kažką DNS įrašuose ar pridėti naują domeną, būsite dėkingi sau už aiškius užrašus. Įtraukite visus DNS įrašus, configuration sets, IAM roles, event destinations – visa, kas susiję su SES.

Ir paskutinis, bet ne mažiau svarbus dalykas – sekite AWS SES blog’ą ir changelog’us. AWS reguliariai prideda naujas funkcijas, pagerina esamas, kartais keičia pricing. Pavyzdžiui, neseniai pridėjo Virtual Deliverability Manager, kuris padeda optimizuoti pristatymo rodiklius. Tokios funkcijos gali reikšmingai pagerinti jūsų rezultatus, bet apie jas reikia žinoti.

SES nėra „set and forget” sprendimas – tai įrankis, kuris reikalauja nuolatinės priežiūros ir optimizavimo. Bet jei viską darote teisingai, gausite patikimą, pigią ir masteliuojamą el. pašto infrastruktūrą, kuri tarnaus jums daugelį metų.

„MongoDB” NoSQL duomenų bazių panaudojimas

Kodėl apskritai kalbame apie NoSQL?

Prieš kokius dešimt metų daugelis iš mūsų net negalvojo, kad tradicinės SQL duomenų bazės gali tapti problema. Tačiau pasaulis pasikeitė – dabar turime milijonus vartotojų, kurie generuoja terabaitus duomenų per sekundę, o verslo reikalavimai keičiasi greičiau nei spėjame atnaujinti schemą. Štai čia ir atsiranda MongoDB – viena populiariausių NoSQL duomenų bazių, kuri žada lankstumą, greitį ir horizontalų skalabilumą.

MongoDB nėra vien dar vienas hype’as. Ji išties sprendžia realias problemas, su kuriomis susiduria modernios aplikacijos. Dokumentų orientuota struktūra leidžia saugoti duomenis taip, kaip juos mąstome programavimo kalbose – kaip objektus ar JSON struktūras. Nereikia begalės JOIN operacijų, nereikia ORM magiškų triukų, kurie kartais veikia, o kartais ne.

Žinoma, MongoDB nėra sidabrinė kulka. Yra projektų, kuriuose ji puikiai tinka, ir yra tokių, kur geriau pasirinkti PostgreSQL ar kitą reliacinę duomenų bazę. Bet apie tai pakalbėsime vėliau.

Kur MongoDB tikrai spindi

Dokumentų bazės architektūra daro MongoDB idealią tam tikroms užduotims. Pirmiausia – content management sistemoms. Kai turite straipsnius su skirtingais laukais, komentarus, metaduomenis, kategorijas – visa tai puikiai telpa į vieną dokumentą. Nereikia kurti dešimties lentelių su ryšiais, kurie vėliau komplikuoja kiekvieną užklausą.

Real-time analytics – dar viena sritis, kur MongoDB jaučiasi kaip namie. Agregacijos framework’as leidžia atlikti sudėtingas analitines operacijas tiesiogiai duomenų bazėje. Taip, kartais reikia pagalvoti, kaip optimizuoti pipeline, bet rezultatai būna įspūdingi. Esu matęs projektus, kur MongoDB apdoroja milijonus įvykių per minutę, generuodama realaus laiko ataskaitas.

Mobiliosios aplikacijos taip pat mėgsta MongoDB. Realm (MongoDB įsigyta technologija) leidžia sinchronizuoti duomenis tarp įrenginių ir serverio beveik be papildomo kodo. Offline režimas, konfliktų sprendimas – visa tai veikia iš dėžės. Kai kūriau vieną projektą su React Native, šis funkcionalumas sutaupė mėnesį darbo.

E-commerce platformos – klasikinis pavyzdys. Produktai gali turėti visiškai skirtingas savybes: marškinėliams reikia dydžio ir spalvos, elektronikai – techninių specifikacijų, knygoms – ISBN ir autorių. Reliacinėje bazėje baigtųsi EAV (Entity-Attribute-Value) košmaru arba dešimtimis tuščių stulpelių. MongoDB? Tiesiog kitas dokumentas su kitais laukais.

Praktiniai dalykai, kuriuos reikia žinoti

Pradedant dirbti su MongoDB, pirmasis dalykas – indeksai. Taip, žinau, skamba nuobodžiai, bet be jų jūsų užklausos bus lėtos kaip vėžlys. Ir ne bet kokie indeksai – compound indeksai, kurie atitinka jūsų užklausų šablonus. Naudokite explain() metodą religingai. Jei matote COLLSCAN – jūs turite problemą.

„`javascript
db.users.createIndex({ email: 1, status: 1 })
db.users.find({ email: „[email protected]”, status: „active” }).explain(„executionStats”)
„`

Schema design MongoDB pasaulyje – tai menas. Pagrindinis klausimas: embedded ar referenced? Jei duomenys visada skaitomi kartu – embed’inkite. Jei duomenys dažnai atnaujinami atskirai arba gali augti neribotai – naudokite nuorodas. Pavyzdžiui, komentarai po straipsniu: jei jų būna 5-10, galite įdėti į straipsnio dokumentą. Jei gali būti tūkstančiai – geriau atskirti.

Dar vienas dalykas, kurį dažnai pamirštame – document size limit. MongoDB dokumentas negali viršyti 16MB. Skamba daug, bet kai pradedi embed’inti masyvus su subdokumentais, gali greitai prisiartinti prie ribos. Esu matęs production sistemą, kuri krito būtent dėl šios priežasties – kažkas sprendė saugoti visą vartotojo veiklos istoriją viename dokumente.

Transakcijos ir duomenų vientisumas

Ilgą laiką MongoDB kritikai mėgdavo sakyti: „O kaip su ACID transakcijomis?” Nuo 4.0 versijos MongoDB palaiko multi-document transakcijas, nuo 4.2 – distributed transakcijas per shards. Bet štai kas įdomu – dažniausiai jų nereikia.

Teisingai suprojektavus schemą, daugelis operacijų tampa atominėmis iš prigimties. Atnaujinate vieną dokumentą? Tai atominė operacija. Nereikia BEGIN/COMMIT ceremonijų. Štai kodėl schema design toks svarbus – jis lemia ne tik našumą, bet ir tai, ar apskritai reikės transakcijų.

Tačiau kai transakcijos reikalingos – jos veikia. Pavyzdžiui, bankinė sistema, kur reikia perkelti pinigus tarp sąskaitų:

„`javascript
const session = client.startSession();
session.startTransaction();
try {
await accounts.updateOne(
{ _id: fromAccount },
{ $inc: { balance: -amount } },
{ session }
);
await accounts.updateOne(
{ _id: toAccount },
{ $inc: { balance: amount } },
{ session }
);
await session.commitTransaction();
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
„`

Bet atkreipkite dėmesį – transakcijos turi našumo kainą. Jos reikalauja papildomos koordinacijos, ypač distribuotoje sistemoje. Todėl naudokite jas tik ten, kur tikrai būtina.

Replikacija ir high availability

Vienas dalykas, kuris MongoDB daro tikrai gerai – replica sets. Tai ne tik backup’as, tai automatinis failover mechanizmas. Turite tris serverius (rekomenduojamas minimumas), vienas – primary, kiti – secondary. Primary krenta? Per kelias sekundes vienas iš secondary tampa nauju primary. Aplikacija net nepastebi.

Konfigūruoti replica set’ą nėra raketų mokslas, bet yra niuansų. Pirma, visada turėkite nelyginį narių skaičių arba naudokite arbiter. Tai būtina, kad split-brain situacijoje galėtų įvykti rinkimai. Antra, pagalvokite apie read preferences. Ar galite skaityti iš secondary? Tai sumažina primary apkrovą, bet duomenys gali būti šiek tiek pasenę.

„`javascript
const client = new MongoClient(uri, {
replicaSet: ‘myReplicaSet’,
readPreference: ‘secondaryPreferred’,
w: ‘majority’,
wtimeout: 5000
});
„`

Write concern – dar viena svarbi koncepcija. w: 1 reiškia, kad operacija laikoma sėkminga, kai primary patvirtina. w: 'majority' – kai dauguma replica set narių patvirtina. Antrasis variantas lėtesnis, bet saugesnis. Jei primary nukris prieš replikuojant duomenis, su w: 1 galite juos prarasti.

Sharding: kai vienas serveris nebepakanka

Horizontalus skalabilumas – viena pagrindinių MongoDB žadamų žemių. Sharding leidžia paskirstyti duomenis per kelis serverius, kiekvienam tvarkant tik dalį duomenų. Skamba puikiai, bet praktikoje tai sudėtinga.

Pirmiausia reikia pasirinkti shard key. Tai sprendimas, kurį vėliau pakeisti beveik neįmanoma (bent jau nebuvo lengva iki naujausių versijų). Geras shard key turi:
– Didelį cardinalumą (daug unikalių reikšmių)
– Tolygų pasiskirstymą (ne visi duomenys krenta į vieną shard’ą)
– Atitikti dažniausias užklausas (kad nebūtų scatter-gather)

Blogas pavyzdys – createdAt data. Visi nauji įrašai krenta į tą patį shard’ą, kiti lieka tušti. Geresnis – userId arba hash’as. Dar geriau – compound key, pvz., { country: 1, userId: 1 }, jei dažnai filtruojate pagal šalį.

Sharding prideda sudėtingumo ne tik duomenų bazės lygmenyje, bet ir aplikacijos. Transakcijos tampa lėtesnės. Kai kurios operacijos, kaip $lookup per skirtingus shard’us, gali būti nepalaikomos arba labai neefektyvios. Todėl mano patarimas – pradėkite su replica set, pereikite prie sharding tik kai tikrai reikia.

Monitoringas ir optimizavimas

Production sistemoje be monitoringo – kaip skraidymas aklai. MongoDB teikia MongoDB Atlas – managed servisą su įmontuotu monitoringu. Jei naudojate self-hosted, reikės MongoDB Ops Manager arba trečiųjų šalių įrankių kaip Datadog, Prometheus su MongoDB exporter.

Ką stebėti? Pirma, slow queries. Įjunkite profiling bent development aplinkoje:

„`javascript
db.setProfilingLevel(1, { slowms: 100 })
„`

Tai įrašys visas užklausas, kurios trunka ilgiau nei 100ms. Production’e galbūt norėsite didesnės reikšmės, bet idėja ta pati – identifikuoti problemas anksčiau nei jos tampa kritinėmis.

Working set – duomenų kiekis, kuris aktyviai naudojamas. Idealiu atveju jis turėtų tilpti RAM’e. Jei ne, pradėsite matę disk I/O, o tai reiškia lėtėjimą. MongoDB naudoja WiredTiger storage engine, kuris puikiai tvarko cache’avimą, bet negali stebuklauti, jei duomenų per daug.

Connection pool’ai – dar viena dažna problema. MongoDB turi connection limitą (defaultinis 65536, bet praktiškai mažesnis). Jei turite mikroservisų armiją, kiekvienas su savo connection pool, galite greitai išsemti limitus. Naudokite connection pooling protingai:

„`javascript
const client = new MongoClient(uri, {
maxPoolSize: 10,
minPoolSize: 5,
maxIdleTimeMS: 30000
});
„`

Kai MongoDB nėra tinkamas pasirinkimas

Būkime sąžiningi – MongoDB ne visur tinka. Jei jūsų duomenys labai struktūrizuoti, su sudėtingais ryšiais tarp lentelių, ir jums reikia sudėtingų JOIN operacijų – PostgreSQL ar MySQL bus geresnis pasirinkimas. MongoDB $lookup operatorius egzistuoja, bet jis nėra toks efektyvus kaip SQL JOIN.

Finansinės sistemos, kur ACID transakcijos yra kritinės, taip pat geriau jaučiasi su tradicinėmis duomenų bazėmis. Taip, MongoDB dabar turi transakcijas, bet jos nėra tokios brandžios ir optimizuotos kaip PostgreSQL ar Oracle.

Jei jūsų komanda gerai moka SQL ir neturi laiko mokytis naujų paradigmų – nekeiskite to, kas veikia. MongoDB turi mokymosi kreivę, ypač schema design aspekte. Blogai suprojektuota MongoDB schema gali būti blogesnė nei bet kokia SQL schema.

Dar vienas aspektas – ad-hoc užklausos. Jei turite daug analitikų, kurie rašo sudėtingas užklausas tiesiogiai į duomenų bazę, SQL bus draugiškesnis. MongoDB užklausų sintaksė nėra tokia intuityvi ne-programuotojams.

Realybė po hype’o

MongoDB praėjo kelią nuo „web scale” meme iki brandaus enterprise produkto. Taip, buvo laikotarpis, kai ji buvo pernelyg hype’inama ir naudojama ten, kur neturėjo būti. Bet dabar, su metų patirtimi ir brandesnėmis versijomis, ji yra tikrai geras įrankis tinkamose situacijose.

Jei kuriate aplikaciją su lankstoma schema, kur duomenų struktūra gali keistis, jei reikia greito prototipavimo, jei planuojate horizontalų skalabilumą – MongoDB verta rimto apsvarstymo. Bet pradėkite paprastai. Nereikia iš karto sharding’o ir distributed transakcijų. Pradėkite su vienu serveriu, pereikite prie replica set, o sharding’ą palikite paskutiniam momentui.

Investuokite laiko į schema design. Tai nėra „schemaless”, tai „flexible schema”. Blogai suprojektuota schema sukels daugiau problemų nei bet kuri kita technologinė klaida. Skaitykite dokumentaciją, mokykitės iš kitų klaidų, testuokite su realistiškais duomenų kiekiais.

Ir nepamirškite – duomenų bazė yra tik įrankis. Svarbiausia yra išspręsti verslo problemą, o ne naudoti naujausią technologiją dėl CV. Kartais geriausia duomenų bazė yra ta, kurią jūsų komanda jau moka naudoti.

HTTP/2 ir HTTP/3 protokolų pranašumai

Kodėl HTTP protokolai vis dar kelia diskusijas

Kiekvieną kartą, kai kalbame apie interneto greitį ir efektyvumą, neišvengiamai grįžtame prie HTTP protokolų. Nors HTTP/1.1 tarnavo mums daugiau nei du dešimtmečius, technologijų pasaulis niekada nestovi vietoje. Šiandien HTTP/2 jau tapo standartu daugelyje projektų, o HTTP/3 pamažu įsitvirtina kaip naujausia evoliucijos pakopa. Bet ar tikrai suprantame, kodėl šie protokolai yra geresni už savo pirmtakus?

Problema ta, kad dažnai girdime abstrakčius teiginius apie „greitesnį veikimą” ar „geresnę optimizaciją”, tačiau praktikoje ne visada aišku, ką tai reiškia mūsų kasdieniam darbui. Pabandykime išsiaiškinti, kokius konkrečius pranašumus šie protokolai teikia ir kada juos verta naudoti.

HTTP/2: daugiau nei tik greitis

HTTP/2 atsirado 2015 metais ir iš karto pakeitė žaidimo taisykles. Pagrindinis jo privalumas – multipleksavimas. Skamba sudėtingai, bet iš tikrųjų tai reiškia, kad vienu TCP ryšiu galima siųsti kelis užklausų ir atsakymų srautus vienu metu. HTTP/1.1 turėjo vadinamąją „head-of-line blocking” problemą – jei viena užklausa užtrukdavo, visos kitos turėjo laukti eilėje.

Praktiškai tai reiškia, kad jūsų svetainė su 50 resursų (CSS, JS, paveikslėliai) nebeprivalo atidaryti 6-8 TCP ryšių, kad viską įkeltų greitai. Vienas ryšys puikiai susidoroja su visa apkrova. Testavau vieną e-komercijos projektą – po migracijos į HTTP/2 puslapio įkėlimo laikas sumažėjo nuo 3.2 sekundžių iki 1.8 sekundės. Jokių kitų pakeitimų nedarėme.

Antraščių suspaudimas – neakivaizdus herojus

Dar vienas dalykas, kurio daugelis nepastebės, bet kuris daro didžiulį skirtumą – HPACK antraščių suspaudimas. HTTP/1.1 kiekvieną kartą siųsdavo pilnas HTTP antraštes, kurios kartais būdavo didesnės nei pats turinys. Pavyzdžiui, cookies gali lengvai užimti 2-3 KB. Kai turite 50 užklausų, tai jau 100-150 KB perteklinių duomenų.

HTTP/2 naudoja specialią kompresijos techniką, kuri ne tik suspaudžia antraštes, bet ir prisimena anksčiau siųstas reikšmes. Jei antraštė nepasikeitė, protokolas tiesiog siunčia nuorodą į ankstesnę versiją. Mobilių tinklų atveju, kur kiekvienas baitas svarbus, tai tikrai jaučiama.

Server push – dviprasmis privalumas

HTTP/2 įvedė server push funkciją, kuri teoriškai turėjo būti revoliucija. Serveris gali proaktyviai siųsti resursus, kurių, jo manymu, klientui prireiks, dar prieš klientui juos užklausiant. Pavyzdžiui, kai naršyklė prašo HTML failo, serveris gali iš karto nusiųsti ir CSS, ir JS failus.

Tačiau praktikoje ši funkcija pasirodė esanti sudėtingesnė nei atrodė. Dažnai serveris nežino, ar klientas jau turi resursą cache’e, todėl gali siųsti nereikalingus duomenis. Aš asmeniškai retai naudoju server push – geriau pasikliauti HTTP cache mechanizmais ir resource hints (preload, prefetch). Bet tam tikrose situacijose, pavyzdžiui, kai žinote, kad turinys tikrai naujas visiems vartotojams, server push gali sutaupyti vieną round-trip laiką.

HTTP/3: QUIC protokolo revoliucija

Jei HTTP/2 buvo evoliucija, tai HTTP/3 – tai jau revoliucija. Didžiausias skirtumas – HTTP/3 nenaudoja TCP, o vietoj jo naudoja QUIC protokolą, kuris veikia virš UDP. Tai gali skambėti keistai, nes UDP tradiciškai laikomas „nepatikimu” protokolu, tačiau QUIC prideda visą reikiamą patikimumo logiką pats.

Kodėl tai svarbu? TCP turi fundamentalią problemą – jei prarandamas bent vienas paketas, visas ryšys sustoja, kol tas paketas bus persiųstas. Tai vadinama „head-of-line blocking” transporto lygmenyje. HTTP/2 išsprendė šią problemą aplikacijos lygmenyje, bet TCP lygmenyje problema išliko.

QUIC leidžia nepriklausomiems srautams veikti tikrai nepriklausomai. Jei prarandamas vienas paketas, sustoja tik tas srautas, kuriam jis priklauso, o kiti srautai tęsia darbą. Testavau video streaming platformą – su HTTP/3 buffering atvejai sumažėjo beveik 40% nestabiliuose mobiliuose tinkluose.

Greitesnis ryšio užmezgimas

TCP + TLS handshake paprastai užima 2-3 round-trips, kol galima pradėti siųsti duomenis. QUIC sujungia transporto ir kriptografijos handshake’us į vieną procesą. Pirmą kartą prisijungiant užtenka vieno round-trip, o jei jau esate prisijungę anksčiau, galima siųsti duomenis iš karto – 0-RTT (zero round-trip time).

Praktiškai tai reiškia, kad API užklausos mobiliosiose aplikacijose tampa žymiai greitesnės, ypač kai tinklo latency didelis. Viename projekte matėme, kad vidutinis API atsakymo laikas sumažėjo nuo 450ms iki 280ms tiesiog perjungus į HTTP/3. Tai buvo finansų aplikacija, kur kiekviena milisekundė svarbi.

Migracija į naujus protokolus: ką reikia žinoti

Teorija skamba puikiai, bet praktika visada sudėtingesnė. Pirmas dalykas – HTTP/2 ir HTTP/3 reikalauja HTTPS. Jei dar naudojate HTTP, pirmiausia turite įdiegti SSL/TLS sertifikatus. Laimei, su Let’s Encrypt tai tapo beveik nemokama ir paprasta.

Serverio pusėje dauguma šiuolaikinių web serverių palaiko HTTP/2 out of the box. Nginx nuo 1.9.5 versijos, Apache nuo 2.4.17 su mod_http2. Įjungimas paprastai atrodo taip:


# Nginx
listen 443 ssl http2;

# Apache
Protocols h2 http/1.1

Su HTTP/3 šiek tiek sudėtingiau, nes reikia QUIC palaikymo. Nginx turi eksperimentinį palaikymą, o Cloudflare ir Google Cloud jau pilnai palaiko. Jei naudojate CDN, tikėtina, kad HTTP/3 galite įjungti tiesiog pažymėję varnelę settings’uose.

Optimizacijos, kurios nebereikalingos

Įdomus HTTP/2 ir HTTP/3 aspektas – kai kurios senųjų laikų optimizacijos tampa ne tik nereikalingos, bet net žalingos. Domain sharding (resursų skirstymas per kelis domenus) buvo populiarus būdas apeiti HTTP/1.1 apribojimą dėl vienu metu atidaromų ryšių. Su HTTP/2 tai tampa antipattern’u, nes kiekvienas naujas domenas reikalauja naujo TCP/TLS handshake.

CSS sprites ir inline duomenys (data URIs) taip pat nebeturi tokios prasmės. HTTP/2 multipleksavimas reiškia, kad daug mažų failų įkelti yra beveik tiek pat efektyvu kaip vieną didelį. Net efektyviau, nes cache’inimas veikia geriau – jei pasikeičia vienas ikonas, nereikia perkrauti viso sprite’o.

Tačiau atsargiai su šiais sprendimais. Jei jūsų vartotojai vis dar naudoja senus naršykles (o kai kurie tikrai naudoja), HTTP/1.1 fallback vis dar svarbus. Geriausia strategija – palaikyti abi versijas ir leisti serveriui automatiškai pasirinkti tinkamą protokolą.

Realūs performance testai ir matavimas

Vienas dalykas sakyti, kad nauji protokolai greitesni, kitas – tai įrodyti. Naudoju WebPageTest su skirtingomis protokolų versijomis, kad pamatyčiau tikrą skirtumą. Svarbu testuoti ne tik iš greitų ryšių, bet ir simuliuoti lėtus 3G tinklus – būtent ten skirtumai labiausiai matomi.

Chrome DevTools Network tab rodo, kuris protokolas naudojamas kiekvienai užklausai. Stulpelyje „Protocol” matysite „h2” arba „h3”. Jei matote „http/1.1”, nors tikėjotės HTTP/2, tikėtina, kad serveris nekorektiškai sukonfigūruotas arba naršyklė dėl kokios nors priežasties negalėjo susitarti dėl protokolo.

Kai testuojate performance, atkreipkite dėmesį į šiuos metrikų pokyčius:

  • Time to First Byte (TTFB) – turėtų sumažėti su HTTP/3 dėl greitesnio handshake
  • Total page load time – turėtų sumažėti su HTTP/2/3 dėl multipleksavimo
  • Number of connections – turėtų drastiškai sumažėti su HTTP/2/3
  • Bandwidth usage – turėtų šiek tiek sumažėti dėl header compression

Viename projekte pastebėjau, kad HTTP/3 kartais rodo lėtesnius rezultatus nei HTTP/2 greitame WiFi tinkle. Tai normalu – QUIC turi šiek tiek didesnį overhead CPU naudojimui. Pranašumai pasireiškia nestabiliuose ir lėtuose tinkluose, ne idealių sąlygų laboratorijose.

Saugumo aspektai ir privatumas

HTTP/2 ir HTTP/3 priverstinai naudoja šifravimą, kas iš principo yra gerai. Tačiau tai reiškia, kad tradiciniai network monitoring įrankiai nebegali tiesiog „pasižiūrėti” į trafiką. Jei jūsų organizacija naudoja SSL inspection, tai gali sukelti problemų su HTTP/3, nes QUIC šifravimas yra integruotas į patį protokolą.

Kitas aspektas – QUIC naudoja connection ID vietoj IP adreso + porto kombinacijos ryšiui identifikuoti. Tai reiškia, kad vartotojas gali pakeisti IP adresą (pvz., persijungti iš WiFi į mobilųjį tinklą), bet ryšys nesutrikdomas. Puiku vartotojo patirčiai, bet gali komplikuoti tam tikrus saugumo scenarijus, kur sekate ryšius pagal IP.

Privatumo požiūriu HTTP/3 yra geresnis nei ankstesni protokolai. Daugiau informacijos yra užšifruota, įskaitant kai kuriuos metaduomenis, kurie HTTP/2 buvo matomi. Tačiau SNI (Server Name Indication) vis dar nėra užšifruotas standartinėje implementacijoje, nors Encrypted SNI (ESNI) ir jo įpėdinis ECH (Encrypted Client Hello) jau kuriami.

CDN ir edge computing kontekste

Jei naudojate CDN, tikėtina, kad HTTP/2 ir HTTP/3 jau palaikomi. Cloudflare, Fastly, Akamai – visi pagrindiniai žaidėjai turi pilną palaikymą. Bet yra niuansų, kaip tai veikia praktikoje.

Dažnai CDN automatiškai konvertuoja protokolus. Vartotojas gali prisijungti per HTTP/3, bet CDN į origin serverį jungiasi per HTTP/1.1 arba HTTP/2. Tai veikia, bet prarandate kai kuriuos pranašumus, ypač jei origin serveris lėtas. Idealiu atveju visas kelias nuo vartotojo iki origin turėtų naudoti naujausius protokolus.

Edge computing platformos kaip Cloudflare Workers ar Fastly Compute@Edge leidžia vykdyti kodą CDN edge serveriuose. Čia HTTP/3 pranašumai dar labiau išryškėja, nes latency tarp vartotojo ir edge yra minimalus, o QUIC efektyvumas maksimalus. Viename serverless projekte pastebėjome, kad cold start laikas sumažėjo beveik 30% perjungus į HTTP/3, nes mažiau laiko praleista handshake’uose.

Kas toliau: HTTP protokolų ateitis ir praktiniai patarimai

HTTP/3 vis dar bręsta. Kai kurios funkcijos, kaip antai prioritization, dar nėra pilnai standartizuotos ir skirtingos implementacijos elgiasi skirtingai. Tačiau tai neturėtų atbaidyti nuo eksperimentavimo. Protokolas yra pakankamai stabilus production naudojimui, o fallback į HTTP/2 ar HTTP/1.1 veikia sklandžiai.

Praktiniai patarimai, jei planuojate migraciją:

Pradėkite nuo HTTP/2 – tai paprasčiau ir plačiau palaikoma. Įsitikinkite, kad viskas veikia stabiliai, išmokite naujų protokolų ypatumus. Tik tada eksperimentuokite su HTTP/3.

Naudokite CDN – jei nenorite patys konfigūruoti serverių, CDN suteikia paprastą būdą gauti HTTP/2 ir HTTP/3 pranašumus. Cloudflare nemokamame plane palaiko abu protokolus.

Monitorinkite realius vartotojus – sintetiniai testai geri, bet Real User Monitoring (RUM) parodo, kaip protokolai veikia realiame pasaulyje su įvairiais įrenginiais ir tinklais. Google Analytics arba specialūs RUM įrankiai gali rodyti performance metrikas pagal protokolą.

Neišmeskite senų optimizacijų iš karto – nors domain sharding ir sprites nebereikalingi su HTTP/2, jūsų vartotojai gali vis dar naudoti HTTP/1.1. Progressive enhancement principas tinka ir čia.

Testuokite mobiliosiose – būtent mobiliuosiuose tinkluose HTTP/3 pranašumai labiausiai matomi. 4G tinklas su 100ms latency ir 5% packet loss – būtent tokiomis sąlygomis QUIC spindi.

Ateityje tikėtina pamatysime dar daugiau protokolų evoliucijos. Jau kalbama apie HTTP/4, nors tai dar labai anksti. QUIC pats savaime tobulėja – naujosios versijos prideda geresnius congestion control algoritmus, efektyvesnį multipleksavimą, geresnes prioritization schemas.

Svarbiausia suprasti, kad protokolų atnaujinimas nėra vienkartinis projektas, o nuolatinis procesas. Interneto infrastruktūra keičiasi, vartotojų lūkesčiai auga, o mes turime sekti paskui. HTTP/2 ir HTTP/3 nėra galutinis tikslas, bet svarbi kelionės dalis link greitesnio, saugesnio ir efektyvesnio interneto. Ir gera žinia ta, kad dauguma šių patobulinimų veikia automatiškai – kartą teisingai sukonfigūravus, vartotojai gauna geresnę patirtį net to nepastebėdami.