[{"data":1,"prerenderedAt":24307},["ShallowReactive",2],{"navigation_docs":3,"-client-server-architecture-grpc-inter-service-communication":5180,"-client-server-architecture-grpc-inter-service-communication-surround":24302},[4,1707,2084,2556,2737,2863,3070,3192,3242,3299,3333,3459,3536,3550,3689,3983,4060,4793,4968,5061,5114,5176],{"title":5,"icon":6,"path":7,"stem":8,"children":9},"C#","i-devicon-csharp","\u002Fcsharp","01.csharp",[10,13,60,90,120,202,219,253,379,404,457,650,1364,1654,1703],{"title":11,"path":7,"stem":12},"C# та .NET","01.csharp\u002Findex",{"title":14,"icon":15,"path":16,"stem":17,"children":18,"page":59},"Fundamentals","i-lucide-book-open","\u002Fcsharp\u002Ffundamentals","01.csharp\u002F01.fundamentals",[19,23,27,31,35,39,43,47,51,55],{"title":20,"path":21,"stem":22},"Вступ до екосистеми .NET","\u002Fcsharp\u002Ffundamentals\u002Fintroduction-to-ecosystem","01.csharp\u002F01.fundamentals\u002F01.introduction-to-ecosystem",{"title":24,"path":25,"stem":26},"Структура програми на C#","\u002Fcsharp\u002Ffundamentals\u002Fprogram-structure","01.csharp\u002F01.fundamentals\u002F02.program-structure",{"title":28,"path":29,"stem":30},"Змінні та Типи Даних","\u002Fcsharp\u002Ffundamentals\u002Fvariables-data-types","01.csharp\u002F01.fundamentals\u002F03.variables-data-types",{"title":32,"path":33,"stem":34},"Масиви","\u002Fcsharp\u002Ffundamentals\u002Farrays","01.csharp\u002F01.fundamentals\u002F04.arrays",{"title":36,"path":37,"stem":38},"Strings & Text Handling","\u002Fcsharp\u002Ffundamentals\u002Fstrings-text-handling","01.csharp\u002F01.fundamentals\u002F05.strings-text-handling",{"title":40,"path":41,"stem":42},"Дати і Час","\u002Fcsharp\u002Ffundamentals\u002Fdates-time-handling","01.csharp\u002F01.fundamentals\u002F06.dates-time-handling",{"title":44,"path":45,"stem":46},"Потік Керування","\u002Fcsharp\u002Ffundamentals\u002Fcontrol-flow","01.csharp\u002F01.fundamentals\u002F07.control-flow",{"title":48,"path":49,"stem":50},"Методи","\u002Fcsharp\u002Ffundamentals\u002Fmethods","01.csharp\u002F01.fundamentals\u002F08.methods",{"title":52,"path":53,"stem":54},"Основи Відлагодження","\u002Fcsharp\u002Ffundamentals\u002Fdebugging-basics","01.csharp\u002F01.fundamentals\u002F09.debugging-basics",{"title":56,"path":57,"stem":58},"Інтерактивна Консоль (Classic)","\u002Fcsharp\u002Ffundamentals\u002Finteractive-console","01.csharp\u002F01.fundamentals\u002F10.interactive-console",false,{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":59},"OOP","i-lucide-box","\u002Fcsharp\u002Foop","01.csharp\u002F02.oop",[66,70,74,78,82,86],{"title":67,"path":68,"stem":69},"Package Management (Управління Пакетами)","\u002Fcsharp\u002Foop\u002Fpackage-management","01.csharp\u002F02.oop\u002F01.package-management",{"title":71,"path":72,"stem":73},"Класи та Об'єкти","\u002Fcsharp\u002Foop\u002Fclasses-objects","01.csharp\u002F02.oop\u002F02.classes-objects",{"title":75,"path":76,"stem":77},"Властивості та Поля","\u002Fcsharp\u002Foop\u002Fproperties-fields","01.csharp\u002F02.oop\u002F03.properties-fields",{"title":79,"path":80,"stem":81},"Стовпи ООП","\u002Fcsharp\u002Foop\u002Foop-pillars","01.csharp\u002F02.oop\u002F04.oop-pillars",{"title":83,"path":84,"stem":85},"Advanced Types","\u002Fcsharp\u002Foop\u002Fadvanced-types","01.csharp\u002F02.oop\u002F05.advanced-types",{"title":87,"path":88,"stem":89},"Namespaces (Простори Імен)","\u002Fcsharp\u002Foop\u002Fnamespaces","01.csharp\u002F02.oop\u002F06.namespaces",{"title":91,"icon":92,"path":93,"stem":94,"children":95,"page":59},"Advanced Core","i-lucide-zap","\u002Fcsharp\u002Fadvanced-core","01.csharp\u002F03.advanced-core",[96,100,104,108,112,116],{"title":97,"path":98,"stem":99},"Generics (Узагальнення)","\u002Fcsharp\u002Fadvanced-core\u002Fgenerics","01.csharp\u002F03.advanced-core\u002F01.generics",{"title":101,"path":102,"stem":103},"Делегати, Події та Лямбда-вирази","\u002Fcsharp\u002Fadvanced-core\u002Fdelegates-events-lambdas","01.csharp\u002F03.advanced-core\u002F02.delegates-events-lambdas",{"title":105,"path":106,"stem":107},"Interfaces Deep Dive (Інтерфейси: Поглиблений Розгляд)","\u002Fcsharp\u002Fadvanced-core\u002Finterfaces-deep-dive","01.csharp\u002F03.advanced-core\u002F03.interfaces-deep-dive",{"title":109,"path":110,"stem":111},"Обробка Винятків","\u002Fcsharp\u002Fadvanced-core\u002Fexception-handling","01.csharp\u002F03.advanced-core\u002F04.exception-handling",{"title":113,"path":114,"stem":115},"Pattern Matching","\u002Fcsharp\u002Fadvanced-core\u002Fpattern-matching","01.csharp\u002F03.advanced-core\u002F05.pattern-matching",{"title":117,"path":118,"stem":119},"Додаткові Можливості C#","\u002Fcsharp\u002Fadvanced-core\u002Fadditional-features","01.csharp\u002F03.advanced-core\u002F06.additional-features",{"title":121,"icon":122,"path":123,"stem":124,"children":125,"page":59},"Architecture Best Practices","i-lucide-building-2","\u002Fcsharp\u002Farchitecture-best-practices","01.csharp\u002F04.architecture-best-practices",[126,130,149,153,157,161,165,169],{"title":127,"path":128,"stem":129},"Software Design Principles (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fsoftware-design-principles","01.csharp\u002F04.architecture-best-practices\u002F01.software-design-principles",{"title":131,"icon":132,"path":133,"stem":134,"children":135,"page":59},"Design Patterns","i-lucide-folder","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns",[136],{"title":137,"icon":132,"path":138,"stem":139,"children":140,"page":59},"Creational","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational",[141,145],{"title":142,"path":143,"stem":144},"Singleton (Одинак)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fsingleton","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F01.singleton",{"title":146,"path":147,"stem":148},"Builder (Будівельник)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fbuilder","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F02.builder",{"title":150,"path":151,"stem":152},"Building Professional CLIs","\u002Fcsharp\u002Farchitecture-best-practices\u002Fbuilding-professional-clis","01.csharp\u002F04.architecture-best-practices\u002F03.building-professional-clis",{"title":154,"path":155,"stem":156},"Validation & Flow Control","\u002Fcsharp\u002Farchitecture-best-practices\u002Fvalidation-flow-control","01.csharp\u002F04.architecture-best-practices\u002F04.validation-flow-control",{"title":158,"path":159,"stem":160},"The Modern .NET Host (Microsoft.Extensions)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fmodern-dotnet-host","01.csharp\u002F04.architecture-best-practices\u002F05.modern-dotnet-host",{"title":162,"path":163,"stem":164},"Data Mapper: Repository та DAO патерни (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part1","01.csharp\u002F04.architecture-best-practices\u002F06.data-mapper-part1",{"title":166,"path":167,"stem":168},"Data Mapper: Repository та DAO патерни (Частина 2)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part2","01.csharp\u002F04.architecture-best-practices\u002F07.data-mapper-part2",{"title":170,"icon":132,"path":171,"stem":172,"children":173,"page":59},"Di Ioc","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc",[174,178,182,186,190,194,198],{"title":175,"path":176,"stem":177},"Проблема залежностей та Інверсія Контролю","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fthe-dependency-problem","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F01.the-dependency-problem",{"title":179,"path":180,"stem":181},"Будуємо власний Service Container","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fbuild-your-own-container","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F02.build-your-own-container",{"title":183,"path":184,"stem":185},"Service Locator: Паттерн та Анти-паттерн","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-locator-pattern","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F03.service-locator-pattern",{"title":187,"path":188,"stem":189},"Паттерни Dependency Injection","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdependency-injection-patterns","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F04.dependency-injection-patterns",{"title":191,"path":192,"stem":193},"Microsoft DI: IServiceCollection та IServiceProvider","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fmicrosoft-di-deep-dive","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F05.microsoft-di-deep-dive",{"title":195,"path":196,"stem":197},"Service Lifetimes та Scopes","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-lifetimes-and-scopes","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F06.service-lifetimes-and-scopes",{"title":199,"path":200,"stem":201},"DI Анти-паттерни та Найкращі Практики","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdi-anti-patterns-and-best-practices","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F07.di-anti-patterns-and-best-practices",{"title":203,"icon":132,"path":204,"stem":205,"children":206,"page":59},"Standard Library","\u002Fcsharp\u002Fstandard-library","01.csharp\u002F05.standard-library",[207,211,215],{"title":208,"path":209,"stem":210},"Collections (Колекції)","\u002Fcsharp\u002Fstandard-library\u002Fcollections","01.csharp\u002F05.standard-library\u002F01.collections",{"title":212,"path":213,"stem":214},"High Performance Types (Високопродуктивні Типи)","\u002Fcsharp\u002Fstandard-library\u002Fhigh-performance-types","01.csharp\u002F05.standard-library\u002F02.high-performance-types",{"title":216,"path":217,"stem":218},"LINQ (Language Integrated Query)","\u002Fcsharp\u002Fstandard-library\u002Flinq","01.csharp\u002F05.standard-library\u002F03.linq",{"title":220,"icon":221,"path":222,"stem":223,"children":224,"page":59},"System Internals Concurrency","i-lucide-server","\u002Fcsharp\u002Fsystem-internals-concurrency","01.csharp\u002F06.system-internals-concurrency",[225,229,233,237,241,245,249],{"title":226,"path":227,"stem":228},"Memory Management","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmemory-management","01.csharp\u002F06.system-internals-concurrency\u002F01.memory-management",{"title":230,"path":231,"stem":232},"Reflection API: System.Type та Метадані","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Freflection-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F02.reflection-fundamentals",{"title":234,"path":235,"stem":236},"Attributes та Dynamic Language Runtime","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fattributes-dynamic","01.csharp\u002F06.system-internals-concurrency\u002F03.attributes-dynamic",{"title":238,"path":239,"stem":240},"Expression Trees: Швидка Альтернатива Рефлексії","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fexpression-trees-compiled","01.csharp\u002F06.system-internals-concurrency\u002F04.expression-trees-compiled",{"title":242,"path":243,"stem":244},"Source Generators: Compile-Time Code Generation","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsource-generators","01.csharp\u002F06.system-internals-concurrency\u002F05.source-generators",{"title":246,"path":247,"stem":248},"Multithreading Fundamentals","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmultithreading-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F06.multithreading-fundamentals",{"title":250,"path":251,"stem":252},"Synchronization Primitives","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsynchronization-primitives","01.csharp\u002F06.system-internals-concurrency\u002F07.synchronization-primitives",{"title":254,"icon":255,"path":256,"stem":257,"children":258,"page":59},"System Programming Windows","i-lucide-cpu","\u002Fcsharp\u002Fsystem-programming-windows","01.csharp\u002F07.system-programming-windows",[259,263,267,271,275,279,283,287,291,295,299,303,307,311,315,319,323,327,331,335,339,343,347,351,355,359,363,367,371,375],{"title":260,"path":261,"stem":262},"Як Працює Операційна Система","\u002Fcsharp\u002Fsystem-programming-windows\u002Fhow-os-works","01.csharp\u002F07.system-programming-windows\u002F01.how-os-works",{"title":264,"path":265,"stem":266},"Процеси в .NET — API та Запуск","\u002Fcsharp\u002Fsystem-programming-windows\u002Fprocesses-in-dotnet","01.csharp\u002F07.system-programming-windows\u002F02.processes-in-dotnet",{"title":268,"path":269,"stem":270},"Процеси в .NET — IPC та Міжпроцесна Комунікація","\u002Fcsharp\u002Fsystem-programming-windows\u002F02a.processes-ipc","01.csharp\u002F07.system-programming-windows\u002F02a.processes-ipc",{"title":272,"path":273,"stem":274},"Application Domains та Збірки — AppDomain і AssemblyLoadContext","\u002Fcsharp\u002Fsystem-programming-windows\u002Fappdomains-assemblies","01.csharp\u002F07.system-programming-windows\u002F03.appdomains-assemblies",{"title":276,"path":277,"stem":278},"Application Domains та Збірки — Plug-in Система з Hot-Reload","\u002Fcsharp\u002Fsystem-programming-windows\u002F03a.appdomains-plugin-system","01.csharp\u002F07.system-programming-windows\u002F03a.appdomains-plugin-system",{"title":280,"path":281,"stem":282},"Потоки — Основи та API Thread","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-fundamentals","01.csharp\u002F07.system-programming-windows\u002F04.thread-fundamentals",{"title":284,"path":285,"stem":286},"Потоки — Lifecycle, Пріоритети та Безпечне Завершення","\u002Fcsharp\u002Fsystem-programming-windows\u002F04a.thread-lifecycle-priorities","01.csharp\u002F07.system-programming-windows\u002F04a.thread-lifecycle-priorities",{"title":288,"path":289,"stem":290},"Проблеми Спільного Стану — Race Condition та Data Race","\u002Fcsharp\u002Fsystem-programming-windows\u002Fshared-state-problems","01.csharp\u002F07.system-programming-windows\u002F05.shared-state-problems",{"title":292,"path":293,"stem":294},"Проблеми Спільного Стану — Memory Model та volatile","\u002Fcsharp\u002Fsystem-programming-windows\u002F05a.shared-state-memory-model","01.csharp\u002F07.system-programming-windows\u002F05a.shared-state-memory-model",{"title":296,"path":297,"stem":298},"Синхронізація — Monitor, lock та еволюція примітивів","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-fundamentals","01.csharp\u002F07.system-programming-windows\u002F06.synchronization-fundamentals",{"title":300,"path":301,"stem":302},"Синхронізація — Наскрізний Приклад та Deadlock Detection","\u002Fcsharp\u002Fsystem-programming-windows\u002F06a.synchronization-walkthrough","01.csharp\u002F07.system-programming-windows\u002F06a.synchronization-walkthrough",{"title":304,"path":305,"stem":306},"Синхронізація — Mutex, Semaphore та Event-Based Primitives","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-advanced","01.csharp\u002F07.system-programming-windows\u002F07.synchronization-advanced",{"title":308,"path":309,"stem":310},"Синхронізація — Interlocked, Volatile та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002F07a.synchronization-advanced-walkthrough","01.csharp\u002F07.system-programming-windows\u002F07a.synchronization-advanced-walkthrough",{"title":312,"path":313,"stem":314},"Interlocked, CAS та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002Finterlocked-cas-lockfree","01.csharp\u002F07.system-programming-windows\u002F08.interlocked-cas-lockfree",{"title":316,"path":317,"stem":318},"Volatile, Memory Model та Spinning","\u002Fcsharp\u002Fsystem-programming-windows\u002F08a.volatile-memory-model","01.csharp\u002F07.system-programming-windows\u002F08a.volatile-memory-model",{"title":320,"path":321,"stem":322},"ThreadPool — Пул Потоків для Ефективного Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-pool","01.csharp\u002F07.system-programming-windows\u002F09.thread-pool",{"title":324,"path":325,"stem":326},"ThreadPool — Просунуті Сценарії та Внутрішня Будова","\u002Fcsharp\u002Fsystem-programming-windows\u002F09a.thread-pool-advanced","01.csharp\u002F07.system-programming-windows\u002F09a.thread-pool-advanced",{"title":328,"path":329,"stem":330},"Concurrent та Immutable Collections","\u002Fcsharp\u002Fsystem-programming-windows\u002Fconcurrent-collections","01.csharp\u002F07.system-programming-windows\u002F10.concurrent-collections",{"title":332,"path":333,"stem":334},"TPL, Task та Композиція — Від Thread до Task","\u002Fcsharp\u002Fsystem-programming-windows\u002Ftpl-parallel-plinq","01.csharp\u002F07.system-programming-windows\u002F11.tpl-parallel-plinq",{"title":336,"path":337,"stem":338},"Parallel Class та PLINQ — Data Parallelism","\u002Fcsharp\u002Fsystem-programming-windows\u002F11a.tpl-parallel-plinq-advanced","01.csharp\u002F07.system-programming-windows\u002F11a.tpl-parallel-plinq-advanced",{"title":340,"path":341,"stem":342},"Async\u002FAwait — Фундамент Асинхронного Програмування","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-fundamentals","01.csharp\u002F07.system-programming-windows\u002F12.async-fundamentals",{"title":344,"path":345,"stem":346},"SynchronizationContext та ConfigureAwait — Контекст Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-context-configureawait","01.csharp\u002F07.system-programming-windows\u002F13.async-context-configureawait",{"title":348,"path":349,"stem":350},"Async — Просунуті Паттерни","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-advanced","01.csharp\u002F07.system-programming-windows\u002F14.async-advanced",{"title":352,"path":353,"stem":354},"System.Threading.Channels — Async Producer-Consumer","\u002Fcsharp\u002Fsystem-programming-windows\u002Fchannels","01.csharp\u002F07.system-programming-windows\u002F15.channels",{"title":356,"path":357,"stem":358},"Асинхронна Синхронізація","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-synchronization","01.csharp\u002F07.system-programming-windows\u002F16.async-synchronization",{"title":360,"path":361,"stem":362},"Unsafe Code та Вказівники","\u002Fcsharp\u002Fsystem-programming-windows\u002Funsafe-code","01.csharp\u002F07.system-programming-windows\u002F17.unsafe-code",{"title":364,"path":365,"stem":366},"P\u002FInvoke та Windows API — Міст між .NET та Native Code","\u002Fcsharp\u002Fsystem-programming-windows\u002Fpinvoke-winapi","01.csharp\u002F07.system-programming-windows\u002F18.pinvoke-winapi",{"title":368,"path":369,"stem":370},"Реєстр Windows — Центральна База Конфігурації Системи","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-registry","01.csharp\u002F07.system-programming-windows\u002F19.windows-registry",{"title":372,"path":373,"stem":374},"Windows Hooks, Hotkeys та Services — Глибока Інтеграція з ОС","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-hooks-services","01.csharp\u002F07.system-programming-windows\u002F20.windows-hooks-services",{"title":376,"path":377,"stem":378},"Системне Програмування C# (Windows) — 07.system-programming-windows","\u002Fcsharp\u002Fsystem-programming-windows\u002Fimplementation_plan","01.csharp\u002F07.system-programming-windows\u002Fimplementation_plan",{"title":380,"icon":132,"path":381,"stem":382,"children":383,"page":59},"Io","\u002Fcsharp\u002Fio","01.csharp\u002F08.io",[384,388,392,396,400],{"title":385,"path":386,"stem":387},"8.1.1. Основи роботи з файловою системою","\u002Fcsharp\u002Fio\u002Ffile-system-basics","01.csharp\u002F08.io\u002F01.file-system-basics",{"title":389,"path":390,"stem":391},"8.1.2. Потоки (Streams) та Серіалізація Даних","\u002Fcsharp\u002Fio\u002Fstreams-serialization","01.csharp\u002F08.io\u002F02.streams-serialization",{"title":393,"path":394,"stem":395},"8.2.1. JSON Serialization з System.Text.Json","\u002Fcsharp\u002Fio\u002Fjson-serialization","01.csharp\u002F08.io\u002F03.json-serialization",{"title":397,"path":398,"stem":399},"8.2.2. XML Serialization та LINQ to XML","\u002Fcsharp\u002Fio\u002Fxml-serialization","01.csharp\u002F08.io\u002F04.xml-serialization",{"title":401,"path":402,"stem":403},"8.2.3. Binary Serialization: MessagePack та Protocol Buffers","\u002Fcsharp\u002Fio\u002Fbinary-serialization","01.csharp\u002F08.io\u002F05.binary-serialization",{"title":405,"icon":132,"path":406,"stem":407,"children":408,"page":59},"Ado Net","\u002Fcsharp\u002Fado-net","01.csharp\u002F09.ado-net",[409,413,417,421,425,429,433,437,441,445,449,453],{"title":410,"path":411,"stem":412},"9.1. Введення в ADO.NET","\u002Fcsharp\u002Fado-net\u002Fintroduction-to-adonet","01.csharp\u002F09.ado-net\u002F01.introduction-to-adonet",{"title":414,"path":415,"stem":416},"9.2. Клас DbConnection — з'єднання з базою даних","\u002Fcsharp\u002Fado-net\u002Fconnection","01.csharp\u002F09.ado-net\u002F02.connection",{"title":418,"path":419,"stem":420},"9.3. Клас DbCommand — виконання SQL-запитів","\u002Fcsharp\u002Fado-net\u002Fcommand-and-queries","01.csharp\u002F09.ado-net\u002F03.command-and-queries",{"title":422,"path":423,"stem":424},"9.4. Клас DbDataReader — ефективне читання даних","\u002Fcsharp\u002Fado-net\u002Fdatareader","01.csharp\u002F09.ado-net\u002F04.datareader",{"title":426,"path":427,"stem":428},"9.5. Параметризовані запити та захист від SQL Injection","\u002Fcsharp\u002Fado-net\u002Fparameters-and-sql-injection","01.csharp\u002F09.ado-net\u002F05.parameters-and-sql-injection",{"title":430,"path":431,"stem":432},"9.6. Транзакції в ADO.NET","\u002Fcsharp\u002Fado-net\u002Ftransactions","01.csharp\u002F09.ado-net\u002F06.transactions",{"title":434,"path":435,"stem":436},"9.7. DbProviderFactory — провайдер-незалежний код","\u002Fcsharp\u002Fado-net\u002Fprovider-factory","01.csharp\u002F09.ado-net\u002F07.provider-factory",{"title":438,"path":439,"stem":440},"9.8. Асинхронний доступ до даних","\u002Fcsharp\u002Fado-net\u002Fasync-data-access","01.csharp\u002F09.ado-net\u002F08.async-data-access",{"title":442,"path":443,"stem":444},"9.9. Від'єднаний режим: DataSet, DataTable, DataRow","\u002Fcsharp\u002Fado-net\u002Fdisconnected-mode-dataset","01.csharp\u002F09.ado-net\u002F09.disconnected-mode-dataset",{"title":446,"path":447,"stem":448},"9.10. DataAdapter — міст між DataSet та базою даних","\u002Fcsharp\u002Fado-net\u002Fdata-adapter","01.csharp\u002F09.ado-net\u002F10.data-adapter",{"title":450,"path":451,"stem":452},"9.11. Data Mapper та Repository: Архітектура доступу до даних","\u002Fcsharp\u002Fado-net\u002Fdata-mapper-repository","01.csharp\u002F09.ado-net\u002F11.data-mapper-repository",{"title":454,"path":455,"stem":456},"9.12. Identity Map, Unit of Work та Specification Pattern","\u002Fcsharp\u002Fado-net\u002Fadvanced-patterns","01.csharp\u002F09.ado-net\u002F12.advanced-patterns",{"title":458,"icon":255,"path":459,"stem":460,"children":461,"page":59},"Ef Core","\u002Fcsharp\u002Fef-core","01.csharp\u002F10.ef-core",[462,466,470,474,478,482,486,490,494,498,502,506,510,514,518,522,526,532,538,542,546,550,554,558,562,566,570,574,578,582,586,590,594,598,602,606,610,614,618,622,626,630,634,638,642,646],{"title":463,"path":464,"stem":465},"Що таке ORM? Від SQL до об'єктів","\u002Fcsharp\u002Fef-core\u002Fwhat-is-orm","01.csharp\u002F10.ef-core\u002F01.what-is-orm",{"title":467,"path":468,"stem":469},"Перший проєкт — від нуля до CRUD","\u002Fcsharp\u002Fef-core\u002Ffirst-project","01.csharp\u002F10.ef-core\u002F02.first-project",{"title":471,"path":472,"stem":473},"DbContext — Серце EF Core","\u002Fcsharp\u002Fef-core\u002Fdbcontext-deep-dive","01.csharp\u002F10.ef-core\u002F03.dbcontext-deep-dive",{"title":475,"path":476,"stem":477},"Провайдери баз даних — Архітектура та Вибір СУБД","\u002Fcsharp\u002Fef-core\u002Fdatabase-providers","01.csharp\u002F10.ef-core\u002F04.database-providers",{"title":479,"path":480,"stem":481},"Конвенції EF Core — Магія без конфігурації","\u002Fcsharp\u002Fef-core\u002Fconventions","01.csharp\u002F10.ef-core\u002F05.conventions",{"title":483,"path":484,"stem":485},"Fluent API та Data Annotations — Явна конфігурація моделі","\u002Fcsharp\u002Fef-core\u002Ffluent-api-vs-annotations","01.csharp\u002F10.ef-core\u002F06.fluent-api-vs-annotations",{"title":487,"path":488,"stem":489},"Зв'язки — One-to-One та One-to-Many","\u002Fcsharp\u002Fef-core\u002Frelationships-basics","01.csharp\u002F10.ef-core\u002F07.relationships-basics",{"title":491,"path":492,"stem":493},"Зв'язки Advanced — Many-to-Many та Складні Сценарії","\u002Fcsharp\u002Fef-core\u002Frelationships-advanced","01.csharp\u002F10.ef-core\u002F08.relationships-advanced",{"title":495,"path":496,"stem":497},"Властивості — Типи, Конвертери, Компаратори (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part1","01.csharp\u002F10.ef-core\u002F09.property-configuration-part1",{"title":499,"path":500,"stem":501},"Властивості — Value Comparers, Generators, Shadow Properties (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part2","01.csharp\u002F10.ef-core\u002F09.property-configuration-part2",{"title":503,"path":504,"stem":505},"Складні типи — Owned Types та Complex Types (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part1","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part1",{"title":507,"path":508,"stem":509},"Складні типи — Complex Types, Keyless Entities, Порівняння (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part2","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part2",{"title":511,"path":512,"stem":513},"JSON Columns — Складні дані у JSON (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part1","01.csharp\u002F10.ef-core\u002F11.json-columns-part1",{"title":515,"path":516,"stem":517},"JSON Columns — Value Comparers, Індекси, Провайдери (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part2","01.csharp\u002F10.ef-core\u002F11.json-columns-part2",{"title":519,"path":520,"stem":521},"Успадкування — Абстрактні класи та TPH (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finheritance-part1","01.csharp\u002F10.ef-core\u002F12.inheritance-part1",{"title":523,"path":524,"stem":525},"Успадкування — TPT, TPC та Порівняння Стратегій (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finheritance-part2","01.csharp\u002F10.ef-core\u002F12.inheritance-part2",{"title":527,"path":528,"stem":529,"children":530},"Індекси, Обмеження та Схема (Частина 1)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part1","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part1",[531],{"title":527,"path":528,"stem":529},{"title":533,"path":534,"stem":535,"children":536},"Індекси, Обмеження та Схема (Частина 2)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part2","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part2",[537],{"title":533,"path":534,"stem":535},{"title":539,"path":540,"stem":541},"Seed Data — Початкові Дані (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fseeding-part1","01.csharp\u002F10.ef-core\u002F14.seeding-part1",{"title":543,"path":544,"stem":545},"Seed Data — SQL-скрипти, Bogus та Стратегії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fseeding-part2","01.csharp\u002F10.ef-core\u002F14.seeding-part2",{"title":547,"path":548,"stem":549},"Global Query Filters — Глобальні Фільтри (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part1","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part1",{"title":551,"path":552,"stem":553},"Global Query Filters — Підводні камені та Інтеграція (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part2","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part2",{"title":555,"path":556,"stem":557},"LINQ-запити в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part1","01.csharp\u002F10.ef-core\u002F16.linq-queries-part1",{"title":559,"path":560,"stem":561},"LINQ-запити в EF Core (Частина 2)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part2","01.csharp\u002F10.ef-core\u002F16.linq-queries-part2",{"title":563,"path":564,"stem":565},"Завантаження Пов'язаних Даних (Частина 1)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part1","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part1",{"title":567,"path":568,"stem":569},"Завантаження Пов'язаних Даних (Частина 2)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part2","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part2",{"title":571,"path":572,"stem":573},"Raw SQL, Views та Stored Procedures (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part1","01.csharp\u002F10.ef-core\u002F18.raw-sql-part1",{"title":575,"path":576,"stem":577},"Raw SQL — Stored Procedures, DbFunction та Bulk Operations (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part2","01.csharp\u002F10.ef-core\u002F18.raw-sql-part2",{"title":579,"path":580,"stem":581},"Продвинуті Запити — Compiled Queries, Bulk та Оптимізація (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part1","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part1",{"title":583,"path":584,"stem":585},"Продвинуті Запити — Query Tags, Bulk та Interceptors (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part2","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part2",{"title":587,"path":588,"stem":589},"Change Tracker — Відстеження Змін (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part1","01.csharp\u002F10.ef-core\u002F20.change-tracking-part1",{"title":591,"path":592,"stem":593},"Change Tracker — Графи Об'єктів та Disconnected (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part2","01.csharp\u002F10.ef-core\u002F20.change-tracking-part2",{"title":595,"path":596,"stem":597},"Збереження Даних та Транзакції (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part1","01.csharp\u002F10.ef-core\u002F21.saving-data-part1",{"title":599,"path":600,"stem":601},"Збереження Даних — Concurrency та Outbox (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part2","01.csharp\u002F10.ef-core\u002F21.saving-data-part2",{"title":603,"path":604,"stem":605},"Конкурентність та Блокування (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part1","01.csharp\u002F10.ef-core\u002F22.concurrency-part1",{"title":607,"path":608,"stem":609},"Конкурентність — Дедлоки та Queue Processing (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part2","01.csharp\u002F10.ef-core\u002F22.concurrency-part2",{"title":611,"path":612,"stem":613},"Міграції в EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part1","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part1",{"title":615,"path":616,"stem":617},"Міграції в EF Core — Основи (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part2","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part2",{"title":619,"path":620,"stem":621},"Міграції — Просунуті Сценарії (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part1","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part1",{"title":623,"path":624,"stem":625},"Міграції — Просунуті Сценарії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part2","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part2",{"title":627,"path":628,"stem":629},"Управління Схемою та Database-First (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part1","01.csharp\u002F10.ef-core\u002F25.schema-management-part1",{"title":631,"path":632,"stem":633},"Управління Схемою та Database-First (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part2","01.csharp\u002F10.ef-core\u002F25.schema-management-part2",{"title":635,"path":636,"stem":637},"Продуктивність EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fperformance-fundamentals-part1","01.csharp\u002F10.ef-core\u002F26.performance-fundamentals-part1",{"title":639,"path":640,"stem":641},"Interceptors в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part1","01.csharp\u002F10.ef-core\u002F29.interceptors-part1",{"title":643,"path":644,"stem":645},"Interceptors в EF Core — Connection, Transaction та Materialization (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part2","01.csharp\u002F10.ef-core\u002F29.interceptors-part2",{"title":647,"path":648,"stem":649},"План вивчення Entity Framework Core — Повний курс","\u002Fcsharp\u002Fef-core\u002Fimplementation_plan","01.csharp\u002F10.ef-core\u002Fimplementation_plan",{"title":651,"icon":652,"path":653,"stem":654,"children":655,"page":59},"ASP.NET","i-devicon-dotnetcore","\u002Fcsharp\u002Faspnet","01.csharp\u002F11.aspnet",[656,730,791,869,927,941,967,1057,1111,1182,1212,1289,1346],{"title":657,"icon":658,"path":659,"stem":660,"children":661,"page":59},"Minimal API","i-lucide-network","\u002Fcsharp\u002Faspnet\u002Fminimal-api","01.csharp\u002F11.aspnet\u002F01.minimal-api",[662,666,670,674,678,682,686,690,694,698,702,706,710,714,718,722,726],{"title":663,"path":664,"stem":665},"Вступ до ASP.NET та еволюція фреймворку","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fintroduction","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F01.introduction",{"title":667,"path":668,"stem":669},"Перший додаток на ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Ffirst-application","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F02.first-application",{"title":671,"path":672,"stem":673},"WebApplication, Builder та Dependency Injection","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fwebapplication-builder","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F03.webapplication-builder",{"title":675,"path":676,"stem":677},"Конвеєр запитів та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frequest-pipeline-middleware","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F04.request-pipeline-middleware",{"title":679,"path":680,"stem":681},"Маршрутизація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F05.routing-basics",{"title":683,"path":684,"stem":685},"Маршрутизація в ASP.NET Core: Розширені можливості","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F06.routing-advanced",{"title":687,"path":688,"stem":689},"Статичні файли в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-files","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F07.static-files",{"title":691,"path":692,"stem":693},"Статичні Активи: MapStaticAssets (ASP.NET Core 9.0)","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-assets","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F08.static-assets",{"title":695,"path":696,"stem":697},"Конфігурація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-fundamentals","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F09.configuration-fundamentals",{"title":699,"path":700,"stem":701},"Конфігурація: Паттерн Options","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-options","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F10.configuration-options",{"title":703,"path":704,"stem":705},"Логування в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F11.logging-basics",{"title":707,"path":708,"stem":709},"Логування: Serilog та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F12.logging-advanced",{"title":711,"path":712,"stem":713},"Управління станом: HttpContext.Items та Cookies","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstate-management","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F13.state-management",{"title":715,"path":716,"stem":717},"Стан сесії: Sessions","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fsession-state","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F14.session-state",{"title":719,"path":720,"stem":721},"Структура проєкту: від хаосу до архітектури","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fproject-structure","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F15.project-structure",{"title":723,"path":724,"stem":725},"Scalar у Minimal API: повний проєкт і Fluent OpenAPI","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fscalar-openapi-fluent","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F16.scalar-openapi-fluent",{"title":727,"path":728,"stem":729},"Swagger \u002F Swashbuckle у Minimal API: окремий класичний шлях","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fswagger-swashbuckle","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F17.swagger-swashbuckle",{"title":731,"icon":658,"path":732,"stem":733,"children":734,"page":59},"API","\u002Fcsharp\u002Faspnet\u002Fapi","01.csharp\u002F11.aspnet\u002F02.api",[735,739,743,747,751,755,759,763,767,771,775,779,783,787],{"title":736,"path":737,"stem":738},"Що таке API. Клієнт-серверна архітектура","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fwhat-is-api","01.csharp\u002F11.aspnet\u002F02.api\u002F01.what-is-api",{"title":740,"path":741,"stem":742},"Формати даних: JSON, XML, TOML та бінарні формати","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fdata-formats","01.csharp\u002F11.aspnet\u002F02.api\u002F02.data-formats",{"title":744,"path":745,"stem":746},"Парадигми API та концепція REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-paradigms-rest","01.csharp\u002F11.aspnet\u002F02.api\u002F03.api-paradigms-rest",{"title":748,"path":749,"stem":750},"HTTP-методи, статус-коди та заголовки","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fhttp-methods-status-codes","01.csharp\u002F11.aspnet\u002F02.api\u002F04.http-methods-status-codes",{"title":752,"path":753,"stem":754},"Організація HTTP API за принципами REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Frest-organizing","01.csharp\u002F11.aspnet\u002F02.api\u002F05.rest-organizing",{"title":756,"path":757,"stem":758},"Номенклатура URL та CRUD-операції","\u002Fcsharp\u002Faspnet\u002Fapi\u002Furl-nomenclature-crud","01.csharp\u002F11.aspnet\u002F02.api\u002F06.url-nomenclature-crud",{"title":760,"path":761,"stem":762},"Правила дизайну: іменування та стандарти","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-naming","01.csharp\u002F11.aspnet\u002F02.api\u002F07.api-design-naming",{"title":764,"path":765,"stem":766},"Валідація, ліміти та обробка помилок","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-validation","01.csharp\u002F11.aspnet\u002F02.api\u002F08.api-design-validation",{"title":768,"path":769,"stem":770},"Обробка помилок у Minimal API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Ferror-handling-http","01.csharp\u002F11.aspnet\u002F02.api\u002F09.error-handling-http",{"title":772,"path":773,"stem":774},"Ідемпотентність та синхронізація стану","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fidempotency-sync","01.csharp\u002F11.aspnet\u002F02.api\u002F10.idempotency-sync",{"title":776,"path":777,"stem":778},"Пагінація та організація списків","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fpagination-lists","01.csharp\u002F11.aspnet\u002F02.api\u002F11.pagination-lists",{"title":780,"path":781,"stem":782},"Безпека API, кешування та інтернаціоналізація","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fsecurity-auth","01.csharp\u002F11.aspnet\u002F02.api\u002F12.security-auth",{"title":784,"path":785,"stem":786},"Процес проєктування API та документування","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-process","01.csharp\u002F11.aspnet\u002F02.api\u002F13.api-design-process",{"title":788,"path":789,"stem":790},"OpenAPI: контракт, специфікація та документація API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fopenapi","01.csharp\u002F11.aspnet\u002F02.api\u002F14.openapi",{"title":792,"icon":793,"path":794,"stem":795,"children":796,"page":59},"Auth","i-lucide-shield-check","\u002Fcsharp\u002Faspnet\u002Fauth","01.csharp\u002F11.aspnet\u002F03.auth",[797,801,805,809,813,817,821,825,829,833,837,841,845,849,853,857,861,865],{"title":798,"path":799,"stem":800},"Основи аутентифікації та авторизації","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauth-fundamentals","01.csharp\u002F11.aspnet\u002F03.auth\u002F01.auth-fundamentals",{"title":802,"path":803,"stem":804},"JWT-аутентифікація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fjwt-authentication","01.csharp\u002F11.aspnet\u002F03.auth\u002F02.jwt-authentication",{"title":806,"path":807,"stem":808},"Авторизація: ролі, політики та resource-based доступ","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauthorization-policies","01.csharp\u002F11.aspnet\u002F03.auth\u002F03.authorization-policies",{"title":810,"path":811,"stem":812},"Cookie-аутентифікація та ASP.NET Core Identity","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcookie-auth-identity","01.csharp\u002F11.aspnet\u002F03.auth\u002F04.cookie-auth-identity",{"title":814,"path":815,"stem":816},"JWT + Refresh Tokens (HttpOnly Cookie)","\u002Fcsharp\u002Faspnet\u002Fauth\u002F04b.identity-auth-jwt","01.csharp\u002F11.aspnet\u002F03.auth\u002F04b.identity-auth-jwt",{"title":818,"path":819,"stem":820},"Identity: Підтвердження Email та Скидання Пароля","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-email-confirmation","01.csharp\u002F11.aspnet\u002F03.auth\u002F05.identity-email-confirmation",{"title":822,"path":823,"stem":824},"Identity: Двофакторна Аутентифікація (2FA)","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-two-factor","01.csharp\u002F11.aspnet\u002F03.auth\u002F06.identity-two-factor",{"title":826,"path":827,"stem":828},"Identity: Внутрішня Архітектура та Кастомізація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-internals","01.csharp\u002F11.aspnet\u002F03.auth\u002F07.identity-internals",{"title":830,"path":831,"stem":832},"OAuth 2.0 та зовнішні провайдери","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-external-providers","01.csharp\u002F11.aspnet\u002F03.auth\u002F08.oauth-external-providers",{"title":834,"path":835,"stem":836},"Безпека на практиці: CORS, HTTPS та захист від атак","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fsecurity-hardening","01.csharp\u002F11.aspnet\u002F03.auth\u002F09.security-hardening",{"title":838,"path":839,"stem":840},"Теорія OAuth 2.0: Поняття, Аналогії та Флоу","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-theory","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oauth-theory",{"title":842,"path":843,"stem":844},"OIDC, OAuth 2.0 та Keycloak в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foidc-keycloak","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oidc-keycloak",{"title":846,"path":847,"stem":848},"API Keys аутентифікація в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fapi-keys","01.csharp\u002F11.aspnet\u002F03.auth\u002F11.api-keys",{"title":850,"path":851,"stem":852},"Rate Limiting та Throttling в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frate-limiting","01.csharp\u002F11.aspnet\u002F03.auth\u002F12.rate-limiting",{"title":854,"path":855,"stem":856},"Refresh Token Rotation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frefresh-token-rotation","01.csharp\u002F11.aspnet\u002F03.auth\u002F13.refresh-token-rotation",{"title":858,"path":859,"stem":860},"Certificate Authentication та mTLS в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcertificate-auth","01.csharp\u002F11.aspnet\u002F03.auth\u002F14.certificate-auth",{"title":862,"path":863,"stem":864},"RBAC, ABAC та ReBAC в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frbac-abac-rebac","01.csharp\u002F11.aspnet\u002F03.auth\u002F15.rbac-abac-rebac",{"title":866,"path":867,"stem":868},"Multi-tenancy та ізоляція даних в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fmulti-tenancy","01.csharp\u002F11.aspnet\u002F03.auth\u002F16.multi-tenancy",{"title":870,"icon":871,"path":872,"stem":873,"children":874,"page":59},"Нотифікації","i-lucide-bell","\u002Fcsharp\u002Faspnet\u002Fnotifications","01.csharp\u002F11.aspnet\u002F04.notifications",[875,879,883,887,891,895,899,903,907,911,915,919,923],{"title":876,"path":877,"stem":878},"In-App нотифікації через базу даних","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fin-app-database-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F01.in-app-database-notifications",{"title":880,"path":881,"stem":882},"Polling: Регулярний запит оновлень","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fpolling","01.csharp\u002F11.aspnet\u002F04.notifications\u002F02.polling",{"title":884,"path":885,"stem":886},"Server-Sent Events: Однострімовий push від сервера","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fserver-sent-events","01.csharp\u002F11.aspnet\u002F04.notifications\u002F03.server-sent-events",{"title":888,"path":889,"stem":890},"WebSockets: Двостороннє з'єднання в реальному часі","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fwebsockets","01.csharp\u002F11.aspnet\u002F04.notifications\u002F04.websockets",{"title":892,"path":893,"stem":894},"SignalR: Абстракція над транспортами реального часу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fsignalr","01.csharp\u002F11.aspnet\u002F04.notifications\u002F05.signalr",{"title":896,"path":897,"stem":898},"Background Services: Фонові задачі в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fbackground-services","01.csharp\u002F11.aspnet\u002F04.notifications\u002F06.background-services",{"title":900,"path":901,"stem":902},"Web Push нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fweb-push","01.csharp\u002F11.aspnet\u002F04.notifications\u002F07.web-push",{"title":904,"path":905,"stem":906},"Email нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Femail-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F08.email-notifications",{"title":908,"path":909,"stem":910},"Порівняння підходів: Як вибрати правильну технологію нотифікацій","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fchoosing-the-right-approach","01.csharp\u002F11.aspnet\u002F04.notifications\u002F09.choosing-the-right-approach",{"title":912,"path":913,"stem":914},"Hangfire: Надійне планування фонових задач","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire","01.csharp\u002F11.aspnet\u002F04.notifications\u002F10.hangfire",{"title":916,"path":917,"stem":918},"Практика: Конвертація зображень у WebP через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-image-webp","01.csharp\u002F11.aspnet\u002F04.notifications\u002F11.hangfire-image-webp",{"title":920,"path":921,"stem":922},"Практика: Підготовка відео до HLS-стрімінгу через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-video-hls","01.csharp\u002F11.aspnet\u002F04.notifications\u002F12.hangfire-video-hls",{"title":924,"path":925,"stem":926},"Telegram-нотифікації: від одного повідомлення до масових розсилок і мульти-канального підходу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Ftelegram-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F13.telegram-notifications",{"title":928,"icon":929,"path":930,"stem":931,"children":932,"page":59},"Інтернаціоналізація","i-lucide-languages","\u002Fcsharp\u002Faspnet\u002Fi18n","01.csharp\u002F11.aspnet\u002F05.i18n",[933,937],{"title":934,"path":935,"stem":936},"Інтернаціоналізація (i18n) у Minimal API: від A до Я","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Finternationalization","01.csharp\u002F11.aspnet\u002F05.i18n\u002F01.internationalization",{"title":938,"path":939,"stem":940},"Humanizer: людиномовні рядки у .NET","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Fhumanizer","01.csharp\u002F11.aspnet\u002F05.i18n\u002F02.humanizer",{"title":942,"icon":943,"path":944,"stem":945,"children":946,"page":59},"Кешування","i-lucide-layers","\u002Fcsharp\u002Faspnet\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching",[947,951,955,959,963],{"title":948,"path":949,"stem":950},"Огляд кешування: чотири рівні і коли що обирати","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching\u002F01.caching",{"title":952,"path":953,"stem":954},"IMemoryCache: кеш в оперативній пам'яті","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fmemory-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F02.memory-cache",{"title":956,"path":957,"stem":958},"IDistributedCache і Redis: розподілений кеш","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fdistributed-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F03.distributed-cache",{"title":960,"path":961,"stem":962},"Response Cache: HTTP-кешування через Cache-Control","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fresponse-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F04.response-cache",{"title":964,"path":965,"stem":966},"Output Cache: серверний кеш HTTP-відповідей (.NET 7+)","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Foutput-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F05.output-cache",{"title":968,"icon":969,"path":970,"stem":971,"children":972,"page":59},"Тестування","i-lucide-test-tube","\u002Fcsharp\u002Faspnet\u002Ftesting","01.csharp\u002F11.aspnet\u002F07.testing",[973,977,981,985,989,993,997,1001,1005,1009,1013,1017,1021,1025,1029,1033,1037,1041,1045,1049,1053],{"title":974,"path":975,"stem":976},"Що таке тестування? Від інтуїції до науки","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-is-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F01.what-is-testing",{"title":978,"path":979,"stem":980},"Піраміда тестування — Стратегія, а не Догма","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-pyramid","01.csharp\u002F11.aspnet\u002F07.testing\u002F02.testing-pyramid",{"title":982,"path":983,"stem":984},"Дві Школи Тестування — Лондон проти Детройту","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-schools","01.csharp\u002F11.aspnet\u002F07.testing\u002F03.testing-schools",{"title":986,"path":987,"stem":988},"TDD та BDD — Тести як Дизайн-інструмент","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftdd-and-bdd","01.csharp\u002F11.aspnet\u002F07.testing\u002F04.tdd-and-bdd",{"title":990,"path":991,"stem":992},"Що саме тестувати — Техніки аналізу та Циклomatична складність","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-to-test","01.csharp\u002F11.aspnet\u002F07.testing\u002F05.what-to-test",{"title":994,"path":995,"stem":996},"Тестові Фреймворки — Навіщо вони і що всередині","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftest-frameworks","01.csharp\u002F11.aspnet\u002F07.testing\u002F06.test-frameworks",{"title":998,"path":999,"stem":1000},"xUnit — Факти, Теорії та Lifecycle тестів","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-basics","01.csharp\u002F11.aspnet\u002F07.testing\u002F07.xunit-basics",{"title":1002,"path":1003,"stem":1004},"xUnit Advanced — Fixtures, Кастомізація та Розширення","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F08.xunit-advanced",{"title":1006,"path":1007,"stem":1008},"Moq — Глибоке занурення в мокування","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fmocking-with-moq","01.csharp\u002F11.aspnet\u002F07.testing\u002F09.mocking-with-moq",{"title":1010,"path":1011,"stem":1012},"Тестування Баз Даних — EF Core, SQLite та Testcontainers","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fdatabase-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F10.database-testing",{"title":1014,"path":1015,"stem":1016},"Integration Testing — Частина 1 [Теорія та WebApplicationFactory]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F11.integration-testing",{"title":1018,"path":1019,"stem":1020},"Інтеграційне тестування — Практика","\u002Fcsharp\u002Faspnet\u002Ftesting\u002F11a.integration-testing-practice","01.csharp\u002F11.aspnet\u002F07.testing\u002F11a.integration-testing-practice",{"title":1022,"path":1023,"stem":1024},"Integration Testing — Частина 2 [Просунуті Сценарії та Testcontainers]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F12.integration-testing-advanced",{"title":1026,"path":1027,"stem":1028},"Професійний Postman: Колекції, Змінні та GitHub Інтеграція","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fpostman-professional","01.csharp\u002F11.aspnet\u002F07.testing\u002F13.postman-professional",{"title":1030,"path":1031,"stem":1032},"HttpClient у Тестах Частина 1: Архітектура та MockHttpMessageHandler","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fhttpclient-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F14.httpclient-testing",{"title":1034,"path":1035,"stem":1036},"HttpClient у Тестах Частина 2: WireMock.Net та Resilience","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwiremock-net","01.csharp\u002F11.aspnet\u002F07.testing\u002F15.wiremock-net",{"title":1038,"path":1039,"stem":1040},"Патерни та Анти-патерни Тестування: Test Smells","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-patterns","01.csharp\u002F11.aspnet\u002F07.testing\u002F16.testing-patterns",{"title":1042,"path":1043,"stem":1044},"Просунуті інструменти: Time, Snapshots та Властивості","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fadvanced-testing-tools","01.csharp\u002F11.aspnet\u002F07.testing\u002F17.advanced-testing-tools",{"title":1046,"path":1047,"stem":1048},"Тестування Архітектури з NetArchTest","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Farchitecture-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F18.architecture-testing",{"title":1050,"path":1051,"stem":1052},"Тестування Продуктивності: BenchmarkDotNet, NBomber та k6","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fperformance-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F19.performance-testing",{"title":1054,"path":1055,"stem":1056},"Залишок плану для курсу \"Тестування ASP.NET Minimal API\"","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fremaining_plan","01.csharp\u002F11.aspnet\u002F07.testing\u002Fremaining_plan",{"title":1058,"icon":1059,"path":1060,"stem":1061,"children":1062,"page":59},"Платежі","i-lucide-credit-card","\u002Fcsharp\u002Faspnet\u002Fpayments","01.csharp\u002F11.aspnet\u002F08.payments",[1063,1067,1071,1075,1079,1083,1087,1091,1095,1099,1103,1107],{"title":1064,"path":1065,"stem":1066},"Основи платіжної інфраструктури","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-fundamentals","01.csharp\u002F11.aspnet\u002F08.payments\u002F01.payment-fundamentals",{"title":1068,"path":1069,"stem":1070},"Методи оплати в Україні","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-methods-ukraine","01.csharp\u002F11.aspnet\u002F08.payments\u002F02.payment-methods-ukraine",{"title":1072,"path":1073,"stem":1074},"PCI DSS та безпека платежів","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpci-dss-security","01.csharp\u002F11.aspnet\u002F08.payments\u002F03.pci-dss-security",{"title":1076,"path":1077,"stem":1078},"Архітектура платіжної підсистеми","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-architecture","01.csharp\u002F11.aspnet\u002F08.payments\u002F04.payment-architecture",{"title":1080,"path":1081,"stem":1082},"Інтеграція LiqPay (ПриватБанк)","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fliqpay-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F05.liqpay-integration",{"title":1084,"path":1085,"stem":1086},"Інтеграція Monobank Acquiring API","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fmonobank-acquiring","01.csharp\u002F11.aspnet\u002F08.payments\u002F06.monobank-acquiring",{"title":1088,"path":1089,"stem":1090},"Інтеграція Stripe","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fstripe-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F07.stripe-integration",{"title":1092,"path":1093,"stem":1094},"Webhooks — глибоке занурення","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fwebhooks-deep-dive","01.csharp\u002F11.aspnet\u002F08.payments\u002F08.webhooks-deep-dive",{"title":1096,"path":1097,"stem":1098},"Підписки та рекурентні платежі","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fsubscriptions-recurring","01.csharp\u002F11.aspnet\u002F08.payments\u002F09.subscriptions-recurring",{"title":1100,"path":1101,"stem":1102},"Повернення коштів та диспути","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Frefunds-disputes","01.csharp\u002F11.aspnet\u002F08.payments\u002F10.refunds-disputes",{"title":1104,"path":1105,"stem":1106},"Тестування платіжних інтеграцій","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Ftesting-payments","01.csharp\u002F11.aspnet\u002F08.payments\u002F11.testing-payments",{"title":1108,"path":1109,"stem":1110},"Чекліст виходу в Production","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fproduction-checklist","01.csharp\u002F11.aspnet\u002F08.payments\u002F12.production-checklist",{"title":1112,"icon":1113,"items":1114,"path":1127,"stem":1128,"children":1129,"page":59},"Популярні бібліотеки","lucide:box",[1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126],"01.fluent-validation","02.mapster","03.erroror-result-pattern","04.serilog","05.mediatr","06.polly","07.health-checks","08.feature-management","09.fluent-email","10.quest-pdf","11.bogus","12.humanizer-guard","\u002Fcsharp\u002Faspnet\u002Flibraries","01.csharp\u002F11.aspnet\u002F09.libraries",[1130,1134,1138,1142,1146,1150,1154,1158,1162,1166,1170,1174,1178],{"title":1131,"path":1132,"stem":1133},"Валідація з FluentValidation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-validation","01.csharp\u002F11.aspnet\u002F09.libraries\u002F01.fluent-validation",{"title":1135,"path":1136,"stem":1137},"Маппінг об","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmapster","01.csharp\u002F11.aspnet\u002F09.libraries\u002F02.mapster",{"title":1139,"path":1140,"stem":1141},"Обробка помилок з ErrorOr та Result Pattern в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ferroror-result-pattern","01.csharp\u002F11.aspnet\u002F09.libraries\u002F03.erroror-result-pattern",{"title":1143,"path":1144,"stem":1145},"Структуроване логування з Serilog в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fserilog","01.csharp\u002F11.aspnet\u002F09.libraries\u002F04.serilog",{"title":1147,"path":1148,"stem":1149},"CQRS та Mediator з MediatR в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmediatr","01.csharp\u002F11.aspnet\u002F09.libraries\u002F05.mediatr",{"title":1151,"path":1152,"stem":1153},"Відмовостійкість з Polly в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fpolly","01.csharp\u002F11.aspnet\u002F09.libraries\u002F06.polly",{"title":1155,"path":1156,"stem":1157},"Health Checks в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhealth-checks","01.csharp\u002F11.aspnet\u002F09.libraries\u002F07.health-checks",{"title":1159,"path":1160,"stem":1161},"Feature Management та Feature Flags в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffeature-management","01.csharp\u002F11.aspnet\u002F09.libraries\u002F08.feature-management",{"title":1163,"path":1164,"stem":1165},"Відправка Email з FluentEmail в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-email","01.csharp\u002F11.aspnet\u002F09.libraries\u002F09.fluent-email",{"title":1167,"path":1168,"stem":1169},"Генерація PDF з QuestPDF в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fquest-pdf","01.csharp\u002F11.aspnet\u002F09.libraries\u002F10.quest-pdf",{"title":1171,"path":1172,"stem":1173},"Генерація тестових даних з Bogus в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fbogus","01.csharp\u002F11.aspnet\u002F09.libraries\u002F11.bogus",{"title":1175,"path":1176,"stem":1177},"Humanizer та Guard Clauses в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhumanizer-guard","01.csharp\u002F11.aspnet\u002F09.libraries\u002F12.humanizer-guard",{"title":1179,"path":1180,"stem":1181},"План модуля 10.libraries — Популярні бібліотеки ASP.NET","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fplan","01.csharp\u002F11.aspnet\u002F09.libraries\u002Fplan",{"title":1183,"icon":1184,"path":1185,"stem":1186,"children":1187,"page":59},"Razor Pages","i-lucide-layout-template","\u002Fcsharp\u002Faspnet\u002Frazor-pages","01.csharp\u002F11.aspnet\u002F10.razor-pages",[1188,1192,1196,1200,1204,1208],{"title":1189,"path":1190,"stem":1191},"Від Minimal API до Razor Pages: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ffrom-minimal-api","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F01.from-minimal-api",{"title":1193,"path":1194,"stem":1195},"PageModel: логіка сторінки Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fpage-model","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F02.page-model",{"title":1197,"path":1198,"stem":1199},"Razor синтаксис: шаблонізатор у .cshtml","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Frazor-syntax","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F03.razor-syntax",{"title":1201,"path":1202,"stem":1203},"Tag Helpers: типізований HTML","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ftag-helpers","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F04.tag-helpers",{"title":1205,"path":1206,"stem":1207},"Форми і валідація: повний цикл обробки даних","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fforms-validation","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F05.forms-validation",{"title":1209,"path":1210,"stem":1211},"Практичний проєкт: TaskManager на Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fproject-task-manager","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F06.project-task-manager",{"title":1213,"path":1214,"stem":1215,"children":1216,"page":59},"ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc","01.csharp\u002F11.aspnet\u002F11.mvc",[1217,1221,1225,1229,1233,1237,1241,1245,1249,1253,1257,1261,1265,1269,1273,1277,1281,1285],{"title":1218,"path":1219,"stem":1220},"Патерн MVC: архітектура, що змінила веб","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-pattern","01.csharp\u002F11.aspnet\u002F11.mvc\u002F01.mvc-pattern",{"title":1222,"path":1223,"stem":1224},"Від Razor Pages до MVC: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffrom-razor-pages","01.csharp\u002F11.aspnet\u002F11.mvc\u002F02.from-razor-pages",{"title":1226,"path":1227,"stem":1228},"Controllers та Actions: серце MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fcontrollers-actions","01.csharp\u002F11.aspnet\u002F11.mvc\u002F03.controllers-actions",{"title":1230,"path":1231,"stem":1232},"Маршрутизація в MVC: Convention vs Attribute Routing","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Frouting-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F04.routing-mvc",{"title":1234,"path":1235,"stem":1236},"Model Binding: від HTTP до C#","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmodel-binding","01.csharp\u002F11.aspnet\u002F11.mvc\u002F05.model-binding",{"title":1238,"path":1239,"stem":1240},"Views, ViewData, ViewBag, TempData і ViewModel","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fviews-viewdata-tempdata","01.csharp\u002F11.aspnet\u002F11.mvc\u002F06.views-viewdata-tempdata",{"title":1242,"path":1243,"stem":1244},"Filters: аспектно-орієнтоване програмування в MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffilters","01.csharp\u002F11.aspnet\u002F11.mvc\u002F07.filters",{"title":1246,"path":1247,"stem":1248},"Areas: структурування великих застосунків","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fareas","01.csharp\u002F11.aspnet\u002F11.mvc\u002F08.areas",{"title":1250,"path":1251,"stem":1252},"View Components: повторювані незалежні блоки UI","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fview-components","01.csharp\u002F11.aspnet\u002F11.mvc\u002F09.view-components",{"title":1254,"path":1255,"stem":1256},"Display та Editor Templates","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fdisplay-editor-templates","01.csharp\u002F11.aspnet\u002F11.mvc\u002F10.display-editor-templates",{"title":1258,"path":1259,"stem":1260},"Валідація: IValidatableObject та FluentValidation","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fvalidation-advanced","01.csharp\u002F11.aspnet\u002F11.mvc\u002F11.validation-advanced",{"title":1262,"path":1263,"stem":1264},"HTMX: інтерактивність через HTML-атрибути","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx","01.csharp\u002F11.aspnet\u002F11.mvc\u002F12.htmx",{"title":1266,"path":1267,"stem":1268},"HTMX у ASP.NET Core MVC: серверна інтеграція","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fajax-htmx-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F13.ajax-htmx-mvc",{"title":1270,"path":1271,"stem":1272},"Практичний проєкт: Каталог товарів з HTMX","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F14.htmx-project",{"title":1274,"path":1275,"stem":1276},"Завантаження та обробка файлів","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffile-upload","01.csharp\u002F11.aspnet\u002F11.mvc\u002F15.file-upload",{"title":1278,"path":1279,"stem":1280},"Глобалізація та Локалізація MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fglobalization-localization","01.csharp\u002F11.aspnet\u002F11.mvc\u002F16.globalization-localization",{"title":1282,"path":1283,"stem":1284},"Підсумковий проєкт: Блог-платформа","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F17.mvc-project",{"title":1286,"path":1287,"stem":1288},"План курсу: ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fplan","01.csharp\u002F11.aspnet\u002F11.mvc\u002Fplan",{"title":1290,"path":1291,"stem":1292,"children":1293,"page":59},"Web Api","\u002Fcsharp\u002Faspnet\u002Fweb-api","01.csharp\u002F11.aspnet\u002F12.web-api",[1294,1298,1302,1306,1310,1314,1318,1322,1326,1330,1334,1338,1342],{"title":1295,"path":1296,"stem":1297},"Від Minimal API до Controller-based API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffrom-minimal-api-to-controllers","01.csharp\u002F11.aspnet\u002F12.web-api\u002F01.from-minimal-api-to-controllers",{"title":1299,"path":1300,"stem":1301},"ControllerBase, ActionResult\u003CT> та Response Types","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontroller-base-actionresult","01.csharp\u002F11.aspnet\u002F12.web-api\u002F02.controller-base-actionresult",{"title":1303,"path":1304,"stem":1305},"Content Negotiation - JSON, XML та власні форматери","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontent-negotiation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F03.content-negotiation",{"title":1307,"path":1308,"stem":1309},"Версіонування API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-versioning","01.csharp\u002F11.aspnet\u002F12.web-api\u002F04.api-versioning",{"title":1311,"path":1312,"stem":1313},"ProblemDetails та структурована обробка помилок","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fproblemdetails-error-handling","01.csharp\u002F11.aspnet\u002F12.web-api\u002F05.problemdetails-error-handling",{"title":1315,"path":1316,"stem":1317},"Фільтри у Web API контексті","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffilters-for-api","01.csharp\u002F11.aspnet\u002F12.web-api\u002F06.filters-for-api",{"title":1319,"path":1320,"stem":1321},"Пагінація, фільтрація та сортування","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fpagination-filtering-sorting","01.csharp\u002F11.aspnet\u002F12.web-api\u002F07.pagination-filtering-sorting",{"title":1323,"path":1324,"stem":1325},"HATEOAS та Resource Expansion","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhateoas-resource-expansion","01.csharp\u002F11.aspnet\u002F12.web-api\u002F08.hateoas-resource-expansion",{"title":1327,"path":1328,"stem":1329},"Гібридна архітектура - Minimal API + Controllers","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fminimal-api-vs-controllers-hybrid","01.csharp\u002F11.aspnet\u002F12.web-api\u002F09.minimal-api-vs-controllers-hybrid",{"title":1331,"path":1332,"stem":1333},"Документація API - Swashbuckle, NSwag та генерація клієнтів","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-documentation-generation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F10.api-documentation-generation",{"title":1335,"path":1336,"stem":1337},"Health Checks та моніторинг API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhealth-checks-monitoring","01.csharp\u002F11.aspnet\u002F12.web-api\u002F11.health-checks-monitoring",{"title":1339,"path":1340,"stem":1341},"Підсумковий проєкт - Production-Ready REST API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fweb-api-project","01.csharp\u002F11.aspnet\u002F12.web-api\u002F12.web-api-project",{"title":1343,"path":1344,"stem":1345},"План курсу: ASP.NET Core Web API (Controllers)","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fplan","01.csharp\u002F11.aspnet\u002F12.web-api\u002Fplan",{"title":1347,"icon":1348,"path":1349,"stem":1350,"children":1351,"page":59},"Моніторинг","i-lucide-activity","\u002Fcsharp\u002Faspnet\u002Fmonitoring","01.csharp\u002F11.aspnet\u002F13.monitoring",[1352,1356,1360],{"title":1353,"path":1354,"stem":1355},"Спостережуваність: від console.log до production-систем","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fobservability-intro","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F01.observability-intro",{"title":1357,"path":1358,"stem":1359},"Health Checks: перший рівень observability","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fhealth-checks","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F02.health-checks",{"title":1361,"path":1362,"stem":1363},"Вбудовані метрики .NET 10 та System.Diagnostics.Metrics","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fdotnet-metrics","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F03.dotnet-metrics",{"title":1365,"icon":1366,"path":1367,"stem":1368,"children":1369,"page":59},"Desktop UI","i-lucide-app-window","\u002Fcsharp\u002Fdesktop-ui","01.csharp\u002F12.desktop-ui",[1370,1374,1378,1382,1386,1390,1394,1398,1402,1406,1410,1414,1418,1422,1426,1430,1434,1438,1442,1446,1450,1454,1458,1462,1466,1470,1474,1478,1482,1486,1490,1494,1498,1502,1506,1510,1514,1518,1522,1526,1530,1534,1538,1542,1546,1550,1554,1558,1562,1566,1570,1574,1578,1582,1586,1590,1594,1598,1602,1606,1610,1614,1618,1622,1626,1630,1634,1638,1642,1646,1650],{"title":1371,"path":1372,"stem":1373},"Що таке десктопна розробка?","\u002Fcsharp\u002Fdesktop-ui\u002Fwhat-is-desktop-dev","01.csharp\u002F12.desktop-ui\u002F01.what-is-desktop-dev",{"title":1375,"path":1376,"stem":1377},"Архітектура WPF — як влаштований графічний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-architecture","01.csharp\u002F12.desktop-ui\u002F02.wpf-architecture",{"title":1379,"path":1380,"stem":1381},"Перший WPF-проєкт — від нуля до вікна","\u002Fcsharp\u002Fdesktop-ui\u002Ffirst-wpf-app","01.csharp\u002F12.desktop-ui\u002F03.first-wpf-app",{"title":1383,"path":1384,"stem":1385},"Перший Avalonia-проєкт: WPF для всіх платформ","\u002Fcsharp\u002Fdesktop-ui\u002F03a.first-avalonia-app","01.csharp\u002F12.desktop-ui\u002F03a.first-avalonia-app",{"title":1387,"path":1388,"stem":1389},"XAML: декларативний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-basics","01.csharp\u002F12.desktop-ui\u002F04.xaml-basics",{"title":1391,"path":1392,"stem":1393},"Fluent UI у WPF — сучасний дизайн Windows 11","\u002Fcsharp\u002Fdesktop-ui\u002F04a.wpf-fluent-ui","01.csharp\u002F12.desktop-ui\u002F04a.wpf-fluent-ui",{"title":1395,"path":1396,"stem":1397},"WPF UI — сучасна бібліотека Fluent контролів","\u002Fcsharp\u002Fdesktop-ui\u002F04b.wpf-ui-library","01.csharp\u002F12.desktop-ui\u002F04b.wpf-ui-library",{"title":1399,"path":1400,"stem":1401},"HandyControl — велика бібліотека UI контролів для WPF","\u002Fcsharp\u002Fdesktop-ui\u002F04c.handycontrol-library","01.csharp\u002F12.desktop-ui\u002F04c.handycontrol-library",{"title":1403,"path":1404,"stem":1405},"Простори імен та ресурси XAML","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-namespaces-resources","01.csharp\u002F12.desktop-ui\u002F05.xaml-namespaces-resources",{"title":1407,"path":1408,"stem":1409},"XAML в Avalonia: ключові відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F05a.avalonia-xaml-differences","01.csharp\u002F12.desktop-ui\u002F05a.avalonia-xaml-differences",{"title":1411,"path":1412,"stem":1413},"Розширення розмітки XAML (Markup Extensions)","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-markup-extensions","01.csharp\u002F12.desktop-ui\u002F06.xaml-markup-extensions",{"title":1415,"path":1416,"stem":1417},"Панелі Layout: StackPanel, WrapPanel, DockPanel","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part1","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part1",{"title":1419,"path":1420,"stem":1421},"Grid, Canvas, UniformGrid","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part2","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part2",{"title":1423,"path":1424,"stem":1425},"Просунуті техніки Layout","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-advanced","01.csharp\u002F12.desktop-ui\u002F08.layout-advanced",{"title":1427,"path":1428,"stem":1429},"Адаптивний Layout та найкращі практики","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-responsive","01.csharp\u002F12.desktop-ui\u002F09.layout-responsive",{"title":1431,"path":1432,"stem":1433},"Layout в Avalonia: відмінності та нові можливості","\u002Fcsharp\u002Fdesktop-ui\u002F09a.layout-avalonia","01.csharp\u002F12.desktop-ui\u002F09a.layout-avalonia",{"title":1435,"path":1436,"stem":1437},"Button, Image, ProgressBar та інші базові контроли","\u002Fcsharp\u002Fdesktop-ui\u002Fbasic-controls","01.csharp\u002F12.desktop-ui\u002F10.basic-controls",{"title":1439,"path":1440,"stem":1441},"Контроли в Avalonia: відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F10a.controls-avalonia","01.csharp\u002F12.desktop-ui\u002F10a.controls-avalonia",{"title":1443,"path":1444,"stem":1445},"Текстові контроли — TextBlock, TextBox, RichTextBox","\u002Fcsharp\u002Fdesktop-ui\u002Ftext-controls","01.csharp\u002F12.desktop-ui\u002F11.text-controls",{"title":1447,"path":1448,"stem":1449},"Контроли вибору — CheckBox, RadioButton, ComboBox, ListBox, DatePicker","\u002Fcsharp\u002Fdesktop-ui\u002Fselection-controls","01.csharp\u002F12.desktop-ui\u002F12.selection-controls",{"title":1451,"path":1452,"stem":1453},"Content Model — GroupBox, Expander, TabControl, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fcontent-controls","01.csharp\u002F12.desktop-ui\u002F13.content-controls",{"title":1455,"path":1456,"stem":1457},"UI\u002FUX принципи десктопних застосунків","\u002Fcsharp\u002Fdesktop-ui\u002F13a.ui-ux-principles","01.csharp\u002F12.desktop-ui\u002F13a.ui-ux-principles",{"title":1459,"path":1460,"stem":1461},"Dependency Properties — Концепція та Value Resolution","\u002Fcsharp\u002Fdesktop-ui\u002Fdependency-properties-part1","01.csharp\u002F12.desktop-ui\u002F14.dependency-properties-part1",{"title":1463,"path":1464,"stem":1465},"Avalonia Property System — StyledProperty та DirectProperty","\u002Fcsharp\u002Fdesktop-ui\u002F14a.avalonia-property-system","01.csharp\u002F12.desktop-ui\u002F14a.avalonia-property-system",{"title":1467,"path":1468,"stem":1469},"Attached Properties — Властивості без меж","\u002Fcsharp\u002Fdesktop-ui\u002Fattached-properties","01.csharp\u002F12.desktop-ui\u002F15.attached-properties",{"title":1471,"path":1472,"stem":1473},"Routed Events — Маршрутизація подій у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Frouted-events","01.csharp\u002F12.desktop-ui\u002F16.routed-events",{"title":1475,"path":1476,"stem":1477},"Data Binding — Від Code-Behind до Декларативності","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part1","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part1",{"title":1479,"path":1480,"stem":1481},"INotifyPropertyChanged — Живе оновлення UI","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part2","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part2",{"title":1483,"path":1484,"stem":1485},"Compiled Bindings в Avalonia — Безпека на етапі компіляції","\u002Fcsharp\u002Fdesktop-ui\u002F17a.avalonia-compiled-bindings","01.csharp\u002F12.desktop-ui\u002F17a.avalonia-compiled-bindings",{"title":1487,"path":1488,"stem":1489},"Просунутий Data Binding — ElementName, RelativeSource, MultiBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-advanced","01.csharp\u002F12.desktop-ui\u002F18.data-binding-advanced",{"title":1491,"path":1492,"stem":1493},"Value Converters — Перетворення типів даних у Data Binding","\u002Fcsharp\u002Fdesktop-ui\u002Fvalue-converters","01.csharp\u002F12.desktop-ui\u002F19.value-converters",{"title":1495,"path":1496,"stem":1497},"Data Templates — Візуалізація об'єктів у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-templates","01.csharp\u002F12.desktop-ui\u002F20.data-templates",{"title":1499,"path":1500,"stem":1501},"Collections Binding Part 1 — ObservableCollection та ItemsControl","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part1","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part1",{"title":1503,"path":1504,"stem":1505},"Collections Binding Part 2 — ICollectionView, Filtering, Sorting та Virtualization","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part2","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part2",{"title":1507,"path":1508,"stem":1509},"MVVM Pattern — Від Spaghetti Code до архітектури","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-pattern","01.csharp\u002F12.desktop-ui\u002F22.mvvm-pattern",{"title":1511,"path":1512,"stem":1513},"ViewModel Implementation — Від BaseViewModel до валідації","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-implementation","01.csharp\u002F12.desktop-ui\u002F23.viewmodel-implementation",{"title":1515,"path":1516,"stem":1517},"Commands — Від event handlers до декларативних команд","\u002Fcsharp\u002Fdesktop-ui\u002Fcommands","01.csharp\u002F12.desktop-ui\u002F24.commands",{"title":1519,"path":1520,"stem":1521},"MVVM Toolkit — MVVM без boilerplate через Source Generators","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-toolkit","01.csharp\u002F12.desktop-ui\u002F25.mvvm-toolkit",{"title":1523,"path":1524,"stem":1525},"Messenger Pattern — Комунікація між ViewModel без прямих посилань","\u002Fcsharp\u002Fdesktop-ui\u002Fmessenger-pattern","01.csharp\u002F12.desktop-ui\u002F26.messenger-pattern",{"title":1527,"path":1528,"stem":1529},"Стилі WPF — CSS для десктопу","\u002Fcsharp\u002Fdesktop-ui\u002Fstyles-basics","01.csharp\u002F12.desktop-ui\u002F27.styles-basics",{"title":1531,"path":1532,"stem":1533},"CSS-like стилі Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F27a.avalonia-css-styling","01.csharp\u002F12.desktop-ui\u002F27a.avalonia-css-styling",{"title":1535,"path":1536,"stem":1537},"Control Templates — Частина 1. Концепція та TemplateBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part1","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part1",{"title":1539,"path":1540,"stem":1541},"Control Templates — Частина 2. Named Parts та ContentPresenter","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part2","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part2",{"title":1543,"path":1544,"stem":1545},"Control Themes в Avalonia — нова ера стилізації","\u002Fcsharp\u002Fdesktop-ui\u002F28a.avalonia-control-themes","01.csharp\u002F12.desktop-ui\u002F28a.avalonia-control-themes",{"title":1547,"path":1548,"stem":1549},"Triggers та Visual State Manager у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Ftriggers-visual-states","01.csharp\u002F12.desktop-ui\u002F29.triggers-visual-states",{"title":1551,"path":1552,"stem":1553},"Pseudo-classes в Avalonia — замість WPF Triggers","\u002Fcsharp\u002Fdesktop-ui\u002F29a.avalonia-pseudo-classes","01.csharp\u002F12.desktop-ui\u002F29a.avalonia-pseudo-classes",{"title":1555,"path":1556,"stem":1557},"Теми та ресурсні словники у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fresources-themes","01.csharp\u002F12.desktop-ui\u002F30.resources-themes",{"title":1559,"path":1560,"stem":1561},"Avalonia Themes — Fluent Design та система тематизації","\u002Fcsharp\u002Fdesktop-ui\u002F30a.avalonia-themes-fluent","01.csharp\u002F12.desktop-ui\u002F30a.avalonia-themes-fluent",{"title":1563,"path":1564,"stem":1565},"Контроли колекцій — глибоке занурення","\u002Fcsharp\u002Fdesktop-ui\u002Fcollection-controls","01.csharp\u002F12.desktop-ui\u002F31.collection-controls",{"title":1567,"path":1568,"stem":1569},"DataGrid — колонки та базове відображення","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part1","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part1",{"title":1571,"path":1572,"stem":1573},"DataGrid — сортування, фільтрація, редагування","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part2","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part2",{"title":1575,"path":1576,"stem":1577},"TreeView та GridView","\u002Fcsharp\u002Fdesktop-ui\u002Ftreeview-listview","01.csharp\u002F12.desktop-ui\u002F33.treeview-listview",{"title":1579,"path":1580,"stem":1581},"Меню, Toolbar, ContextMenu, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fmenus-toolbars","01.csharp\u002F12.desktop-ui\u002F34.menus-toolbars",{"title":1583,"path":1584,"stem":1585},"Навігація та керування вікнами. Частина 1: вікна та сторінки","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part1","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part1",{"title":1587,"path":1588,"stem":1589},"Навігація та керування вікнами. Частина 2: MVVM-навігація","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part2","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part2",{"title":1591,"path":1592,"stem":1593},"Avalonia — Навігація та діалоги","\u002Fcsharp\u002Fdesktop-ui\u002F35a.avalonia-navigation-dialogs","01.csharp\u002F12.desktop-ui\u002F35a.avalonia-navigation-dialogs",{"title":1595,"path":1596,"stem":1597},"Діалоги та File Pickers у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdialogs-file-pickers","01.csharp\u002F12.desktop-ui\u002F36.dialogs-file-pickers",{"title":1599,"path":1600,"stem":1601},"UserControl: компонентний підхід у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fuser-controls","01.csharp\u002F12.desktop-ui\u002F37.user-controls",{"title":1603,"path":1604,"stem":1605},"Custom Controls: Lookless Controls у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fcustom-controls","01.csharp\u002F12.desktop-ui\u002F38.custom-controls",{"title":1607,"path":1608,"stem":1609},"Avalonia TemplatedControl — Lookless Controls","\u002Fcsharp\u002Fdesktop-ui\u002F38a.avalonia-templated-controls","01.csharp\u002F12.desktop-ui\u002F38a.avalonia-templated-controls",{"title":1611,"path":1612,"stem":1613},"Анімації у WPF: Storyboard та Easing Functions","\u002Fcsharp\u002Fdesktop-ui\u002Fanimations-transitions","01.csharp\u002F12.desktop-ui\u002F39.animations-transitions",{"title":1615,"path":1616,"stem":1617},"Анімації в Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F39a.avalonia-animations","01.csharp\u002F12.desktop-ui\u002F39a.avalonia-animations",{"title":1619,"path":1620,"stem":1621},"2D Графіка та Мультимедіа у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fmedia-graphics","01.csharp\u002F12.desktop-ui\u002F40.media-graphics",{"title":1623,"path":1624,"stem":1625},"Dependency Injection у WPF та Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Fdi-integration","01.csharp\u002F12.desktop-ui\u002F41.di-integration",{"title":1627,"path":1628,"stem":1629},"SQLite та EF Core у десктопних додатках","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part1","01.csharp\u002F12.desktop-ui\u002F42.data-persistence-part1",{"title":1631,"path":1632,"stem":1633},"Repository Pattern та Unit of Work","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part2","01.csharp\u002F12.desktop-ui\u002F43.data-persistence-part2",{"title":1635,"path":1636,"stem":1637},"Тестування ViewModels","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-testing","01.csharp\u002F12.desktop-ui\u002F44.viewmodel-testing",{"title":1639,"path":1640,"stem":1641},"Avalonia Headless Testing — тестування UI без вікон","\u002Fcsharp\u002Fdesktop-ui\u002F44a.avalonia-headless-testing","01.csharp\u002F12.desktop-ui\u002F44a.avalonia-headless-testing",{"title":1643,"path":1644,"stem":1645},"Кросплатформна розробка з Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-cross-platform","01.csharp\u002F12.desktop-ui\u002F45.avalonia-cross-platform",{"title":1647,"path":1648,"stem":1649},"Пакування та розгортання Avalonia додатків","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F46.avalonia-packaging-deployment",{"title":1651,"path":1652,"stem":1653},"Розгортання WPF застосунків","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F47.wpf-packaging-deployment",{"title":1655,"icon":658,"path":1656,"stem":1657,"children":1658,"page":59},"Network Programming","\u002Fcsharp\u002Fnetwork-programming","01.csharp\u002F13.network-programming",[1659,1663,1667,1671,1675,1679,1683,1687,1691,1695,1699],{"title":1660,"path":1661,"stem":1662},"Основи комп'ютерних мереж","\u002Fcsharp\u002Fnetwork-programming\u002Ffoundations","01.csharp\u002F13.network-programming\u002F01.foundations",{"title":1664,"path":1665,"stem":1666},"Модель OSI та стек TCP\u002FIP","\u002Fcsharp\u002Fnetwork-programming\u002Fosi-model","01.csharp\u002F13.network-programming\u002F02.osi-model",{"title":1668,"path":1669,"stem":1670},"IP-протокол та адресація","\u002Fcsharp\u002Fnetwork-programming\u002Fip-addressing","01.csharp\u002F13.network-programming\u002F03.ip-addressing",{"title":1672,"path":1673,"stem":1674},"UDP — протокол без з'єднання","\u002Fcsharp\u002Fnetwork-programming\u002Fudp","01.csharp\u002F13.network-programming\u002F05.udp",{"title":1676,"path":1677,"stem":1678},"UDP Broadcast та Multicast","\u002Fcsharp\u002Fnetwork-programming\u002Fudp-broadcast-multicast","01.csharp\u002F13.network-programming\u002F06.udp-broadcast-multicast",{"title":1680,"path":1681,"stem":1682},"HTTP — протокол вебу","\u002Fcsharp\u002Fnetwork-programming\u002Fhttp-fundamentals","01.csharp\u002F13.network-programming\u002F07.http-fundamentals",{"title":1684,"path":1685,"stem":1686},"HttpListener — вбудований HTTP-сервер .NET","\u002Fcsharp\u002Fnetwork-programming\u002F07a.http-listener","01.csharp\u002F13.network-programming\u002F07a.http-listener",{"title":1688,"path":1689,"stem":1690},"HTTP Advanced — cookies, аутентифікація та HTTPS","\u002Fcsharp\u002Fnetwork-programming\u002Fhttp-advanced","01.csharp\u002F13.network-programming\u002F08.http-advanced",{"title":1692,"path":1693,"stem":1694},"SMTP та протоколи електронної пошти","\u002Fcsharp\u002Fnetwork-programming\u002Fsmtp","01.csharp\u002F13.network-programming\u002F09.smtp",{"title":1696,"path":1697,"stem":1698},"WebSocket — повнодуплексний протокол реального часу","\u002Fcsharp\u002Fnetwork-programming\u002Fwebsockets","01.csharp\u002F13.network-programming\u002F10.websockets",{"title":1700,"path":1701,"stem":1702},"TLS\u002FSSL — криптографічний захист мережевих з'єднань","\u002Fcsharp\u002Fnetwork-programming\u002Ftls-ssl","01.csharp\u002F13.network-programming\u002F11.tls-ssl",{"title":1704,"path":1705,"stem":1706},"C# & .NET: The Ultimate Roadmap","\u002Fcsharp\u002Froadmap","01.csharp\u002Froadmap",{"title":1708,"icon":1709,"path":1710,"stem":1711,"children":1712,"page":59},"C++","i-devicon-cplusplus","\u002Fcpp","02.cpp",[1713,1717,1721,1725,1729,1733,1737,1741,1745,1748,1752,1756,1760,1764,1768,1772,1776,1780,1784,1788,1792,1796,1800,1804,1808,1812,1816,1820,1824,1828,1832,1836,1840,1844,1848,1852,1856,1860,1864,1868,1872,1876,1880,1884,1888,1892,1896,1900,1904,1908,1912,1916,1920,1924,1928,1932,1936,1940,1944,1948,1952,1956,1960,1964,1968,1972,1976,1980,1984,1988,1992,1996,2000,2004,2008,2012,2016,2020,2024,2028,2032,2036,2040,2044,2048,2052,2056,2060,2064,2068,2072,2076,2080],{"title":1714,"path":1715,"stem":1716},"Вступ у програмування та алгоритми","\u002Fcpp\u002Fintro-algorithms","02.cpp\u002F01.intro-algorithms",{"title":1718,"path":1719,"stem":1720},"Code Style: угоди про оформлення коду","\u002Fcpp\u002Fcode-style","02.cpp\u002F02.code-style",{"title":1722,"path":1723,"stem":1724},"Середовище розробки та перший проєкт","\u002Fcpp\u002Fide-setup","02.cpp\u002F03.ide-setup",{"title":1726,"path":1727,"stem":1728},"Вивід даних на екран","\u002Fcpp\u002Fdata-output","02.cpp\u002F04.data-output",{"title":1730,"path":1731,"stem":1732},"Типи даних, змінні та константи","\u002Fcpp\u002Fdata-types-variables","02.cpp\u002F05.data-types-variables",{"title":1734,"path":1735,"stem":1736},"Ввід даних з клавіатури","\u002Fcpp\u002Fdata-input","02.cpp\u002F06.data-input",{"title":1738,"path":1739,"stem":1740},"Оператори, перетворення типів та логічні операції","\u002Fcpp\u002Foperators-type-conversion","02.cpp\u002F07.operators-type-conversion",{"title":1742,"path":1743,"stem":1744},"Цикли","\u002Fcpp\u002Floops","02.cpp\u002F08.loops",{"title":32,"path":1746,"stem":1747},"\u002Fcpp\u002Farrays","02.cpp\u002F09.arrays",{"title":1749,"path":1750,"stem":1751},"Алгоритми сортування та аналіз складності","\u002Fcpp\u002Fsorting","02.cpp\u002F10.sorting",{"title":1753,"path":1754,"stem":1755},"Алгоритми пошуку","\u002Fcpp\u002Fsearching","02.cpp\u002F11.searching",{"title":1757,"path":1758,"stem":1759},"Функції: основи","\u002Fcpp\u002Ffunctions-basics","02.cpp\u002F12.functions-basics",{"title":1761,"path":1762,"stem":1763},"Функції: прототипи, область видимості та додаткові можливості","\u002Fcpp\u002Ffunctions-scope","02.cpp\u002F13.functions-scope",{"title":1765,"path":1766,"stem":1767},"Функції: перевантаження та шаблони","\u002Fcpp\u002Ffunctions-overloading-templates","02.cpp\u002F14.functions-overloading-templates",{"title":1769,"path":1770,"stem":1771},"Вказівники: основи","\u002Fcpp\u002Fpointers-basics","02.cpp\u002F15.pointers-basics",{"title":1773,"path":1774,"stem":1775},"Посилання (References)","\u002Fcpp\u002Freferences","02.cpp\u002F16.references",{"title":1777,"path":1778,"stem":1779},"Вказівники, const і масиви","\u002Fcpp\u002Fpointers-const-arrays","02.cpp\u002F17.pointers-const-arrays",{"title":1781,"path":1782,"stem":1783},"Адресна арифметика","\u002Fcpp\u002Fpointer-arithmetic","02.cpp\u002F18.pointer-arithmetic",{"title":1785,"path":1786,"stem":1787},"Динамічна пам'ять","\u002Fcpp\u002Fdynamic-memory","02.cpp\u002F19.dynamic-memory",{"title":1789,"path":1790,"stem":1791},"Вказівники типу void","\u002Fcpp\u002Fvoid-pointers","02.cpp\u002F20.void-pointers",{"title":1793,"path":1794,"stem":1795},"Вказівники на вказівники","\u002Fcpp\u002Fpointers-to-pointers","02.cpp\u002F21.pointers-to-pointers",{"title":1797,"path":1798,"stem":1799},"Оператор доступу до членів через вказівник (->)","\u002Fcpp\u002Fmember-access-operator","02.cpp\u002F22.member-access-operator",{"title":1801,"path":1802,"stem":1803},"Цикл for-each (Range-based for)","\u002Fcpp\u002Fforeach-loop","02.cpp\u002F23.foreach-loop",{"title":1805,"path":1806,"stem":1807},"Вказівники на функції","\u002Fcpp\u002Ffunction-pointers","02.cpp\u002F24.function-pointers",{"title":1809,"path":1810,"stem":1811},"Лямбда-вирази","\u002Fcpp\u002Flambdas","02.cpp\u002F25.lambdas",{"title":1813,"path":1814,"stem":1815},"Лямбда-захоплення","\u002Fcpp\u002Flambda-captures","02.cpp\u002F26.lambda-captures",{"title":1817,"path":1818,"stem":1819},"Еліпсис","\u002Fcpp\u002Fellipsis","02.cpp\u002F27.ellipsis",{"title":1821,"path":1822,"stem":1823},"Безпечні альтернативи еліпсису","\u002Fcpp\u002F27a.ellipsis","02.cpp\u002F27a.ellipsis",{"title":1825,"path":1826,"stem":1827},"Аргументи командного рядка","\u002Fcpp\u002Fcommand-line-arguments","02.cpp\u002F28.command-line-arguments",{"title":1829,"path":1830,"stem":1831},"Перерахування (enum)","\u002Fcpp\u002Fenum","02.cpp\u002F29.enum",{"title":1833,"path":1834,"stem":1835},"Класи-перерахування (enum class)","\u002Fcpp\u002Fenum-class","02.cpp\u002F30.enum-class",{"title":1837,"path":1838,"stem":1839},"Псевдоніми типів (typedef і using)","\u002Fcpp\u002Ftype-aliases","02.cpp\u002F31.type-aliases",{"title":1841,"path":1842,"stem":1843},"Системи числення та двійкова арифметика","\u002Fcpp\u002Fnumber-systems","02.cpp\u002F32.number-systems",{"title":1845,"path":1846,"stem":1847},"Структури (struct): агрегування даних","\u002Fcpp\u002Fstruct","02.cpp\u002F33.struct",{"title":1849,"path":1850,"stem":1851},"Структури у функціях","\u002Fcpp\u002Fstruct-functions","02.cpp\u002F34.struct-functions",{"title":1853,"path":1854,"stem":1855},"Масиви структур і вкладені структури","\u002Fcpp\u002Fstruct-arrays","02.cpp\u002F35.struct-arrays",{"title":1857,"path":1858,"stem":1859},"Патерни struct та межі застосування","\u002Fcpp\u002Fstruct-patterns","02.cpp\u002F36.struct-patterns",{"title":1861,"path":1862,"stem":1863},"Символи та таблиця ASCII","\u002Fcpp\u002Fascii-characters","02.cpp\u002F37.ascii-characters",{"title":1865,"path":1866,"stem":1867},"Unicode та кодування UTF","\u002Fcpp\u002Funicode-utf","02.cpp\u002F38.unicode-utf",{"title":1869,"path":1870,"stem":1871},"C-style рядки","\u002Fcpp\u002Fc-strings","02.cpp\u002F39.c-strings",{"title":1873,"path":1874,"stem":1875},"Вступ до std::string","\u002Fcpp\u002Fstd-string-intro","02.cpp\u002F40.std-string-intro",{"title":1877,"path":1878,"stem":1879},"Довжина, ємність та доступ до символів std::string","\u002Fcpp\u002Fstd-string-capacity-access","02.cpp\u002F41.std-string-capacity-access",{"title":1881,"path":1882,"stem":1883},"Модифікація std::string: присвоювання, додавання, вставка, видалення та заміна","\u002Fcpp\u002Fstd-string-modification","02.cpp\u002F42.std-string-modification",{"title":1885,"path":1886,"stem":1887},"Пошук у std::string: find, npos та практичні патерни","\u002Fcpp\u002Fstd-string-search","02.cpp\u002F43.std-string-search",{"title":1889,"path":1890,"stem":1891},"std::string_view: невласницький погляд на рядок без копіювання","\u002Fcpp\u002Fstd-string-view","02.cpp\u002F44.std-string-view",{"title":1893,"path":1894,"stem":1895},"Об'єднання (union): один блок пам'яті, кілька інтерпретацій","\u002Fcpp\u002Funion","02.cpp\u002F45.union",{"title":1897,"path":1898,"stem":1899},"Організація коду: файли, препроцесор, простори імен","\u002Fcpp\u002Fmultifile-programs","02.cpp\u002F46.multifile-programs",{"title":1901,"path":1902,"stem":1903},"Робота з файлами: C-стиль (stdio.h)","\u002Fcpp\u002Fc-style-files","02.cpp\u002F47.c-style-files",{"title":1905,"path":1906,"stem":1907},"Робота з файлами: C++-стиль (fstream)","\u002Fcpp\u002Fcpp-style-files","02.cpp\u002F48.cpp-style-files",{"title":1909,"path":1910,"stem":1911},"Введення в ООП: від процедур до об'єктів","\u002Fcpp\u002Foop-intro","02.cpp\u002F49.oop-intro",{"title":1913,"path":1914,"stem":1915},"Класи, об'єкти та методи","\u002Fcpp\u002Fclasses-objects-methods","02.cpp\u002F50.classes-objects-methods",{"title":1917,"path":1918,"stem":1919},"Специфікатори доступу та інкапсуляція","\u002Fcpp\u002Faccess-specifiers-encapsulation","02.cpp\u002F51.access-specifiers-encapsulation",{"title":1921,"path":1922,"stem":1923},"Конструктори: гарантована ініціалізація об'єктів","\u002Fcpp\u002Fconstructors","02.cpp\u002F52.constructors",{"title":1925,"path":1926,"stem":1927},"Списки ініціалізації та делегуючі конструктори","\u002Fcpp\u002Finit-lists-delegating","02.cpp\u002F53.init-lists-delegating",{"title":1929,"path":1930,"stem":1931},"Деструктори та ідіома RAII","\u002Fcpp\u002Fdestructors-raii","02.cpp\u002F54.destructors-raii",{"title":1933,"path":1934,"stem":1935},"Прихований покажчик this","\u002Fcpp\u002Fthis-pointer","02.cpp\u002F55.this-pointer",{"title":1937,"path":1938,"stem":1939},"Розподіл класу на заголовковий файл та файл реалізації","\u002Fcpp\u002Fclass-header-source","02.cpp\u002F56.class-header-source",{"title":1941,"path":1942,"stem":1943},"Класи та const","\u002Fcpp\u002Fconst-classes","02.cpp\u002F57.const-classes",{"title":1945,"path":1946,"stem":1947},"Статичні члени класу","\u002Fcpp\u002Fstatic-members","02.cpp\u002F58.static-members",{"title":1949,"path":1950,"stem":1951},"Конструктор копіювання та запобігання копіюванню","\u002Fcpp\u002Fcopy-constructor","02.cpp\u002F59.copy-constructor",{"title":1953,"path":1954,"stem":1955},"Дружні функції та класи","\u002Fcpp\u002Ffriend-functions-classes","02.cpp\u002F60.friend-functions-classes",{"title":1957,"path":1958,"stem":1959},"Анонімні об'єкти та вкладені типи","\u002Fcpp\u002Fanonymous-objects-nested-types","02.cpp\u002F61.anonymous-objects-nested-types",{"title":1961,"path":1962,"stem":1963},"Вимірювання часу виконання: RAII-таймер","\u002Fcpp\u002Ftiming-raii-timer","02.cpp\u002F62.timing-raii-timer",{"title":1965,"path":1966,"stem":1967},"Перевантаження операторів: основи та арифметика","\u002Fcpp\u002Foperator-overloading-basics","02.cpp\u002F63.operator-overloading-basics",{"title":1969,"path":1970,"stem":1971},"Перевантаження операторів: методи класу та спеціальні оператори","\u002Fcpp\u002Foperator-overloading-methods","02.cpp\u002F64.operator-overloading-methods",{"title":1973,"path":1974,"stem":1975},"Приведення типів, explicit та delete","\u002Fcpp\u002Ftypecast-explicit-delete","02.cpp\u002F65.typecast-explicit-delete",{"title":1977,"path":1978,"stem":1979},"Глибоке копіювання та оператор присвоювання","\u002Fcpp\u002Fdeep-copy-assignment","02.cpp\u002F66.deep-copy-assignment",{"title":1981,"path":1982,"stem":1983},"Зв'язки між об'єктами: композиція та агрегація","\u002Fcpp\u002Fcomposition-aggregation","02.cpp\u002F67.composition-aggregation",{"title":1985,"path":1986,"stem":1987},"Асоціація та залежність між об","\u002Fcpp\u002Fassociation-dependency","02.cpp\u002F68.association-dependency",{"title":1989,"path":1990,"stem":1991},"Контейнерні класи та `std::initializer_list`","\u002Fcpp\u002Fcontainer-classes-initializer-list","02.cpp\u002F69.container-classes-initializer-list",{"title":1993,"path":1994,"stem":1995},"Вступ до наслідування: базові поняття та відношення «is-a»","\u002Fcpp\u002Finheritance-intro","02.cpp\u002F70.inheritance-intro",{"title":1997,"path":1998,"stem":1999},"Конструктори, деструктори та порядок ініціалізації в ієрархіях","\u002Fcpp\u002Finheritance-constructors-destructors","02.cpp\u002F71.inheritance-constructors-destructors",{"title":2001,"path":2002,"stem":2003},"Специфікатор `protected` та види наслідування","\u002Fcpp\u002Finheritance-access-specifiers","02.cpp\u002F72.inheritance-access-specifiers",{"title":2005,"path":2006,"stem":2007},"Перевизначення методів та приховування імен","\u002Fcpp\u002Fmethod-overriding-hiding","02.cpp\u002F73.method-overriding-hiding",{"title":2009,"path":2010,"stem":2011},"Множинне наслідування та проблема ромба","\u002Fcpp\u002Fmultiple-inheritance-diamond","02.cpp\u002F74.multiple-inheritance-diamond",{"title":2013,"path":2014,"stem":2015},"Вказівники на базовий клас та віртуальні функції","\u002Fcpp\u002Fvirtual-functions-polymorphism","02.cpp\u002F75.virtual-functions-polymorphism",{"title":2017,"path":2018,"stem":2019},"Специфікатори override, final та віртуальні деструктори","\u002Fcpp\u002Foverride-final-virtual-destructors","02.cpp\u002F76.override-final-virtual-destructors",{"title":2021,"path":2022,"stem":2023},"Внутрішні механізми поліморфізму: vtable, vptr та зв'язування","\u002Fcpp\u002Fvirtual-table-binding","02.cpp\u002F77.virtual-table-binding",{"title":2025,"path":2026,"stem":2027},"Абстрактні класи та чисті віртуальні функції","\u002Fcpp\u002Fabstract-classes-interfaces","02.cpp\u002F78.abstract-classes-interfaces",{"title":2029,"path":2030,"stem":2031},"Зрізка об'єктів, віртуальне наслідування та поліморфний вивід","\u002Fcpp\u002Fslicing-virtual-base-io","02.cpp\u002F79.slicing-virtual-base-io",{"title":2033,"path":2034,"stem":2035},"Динамічне приведення типів (`dynamic_cast`) та RTTI","\u002Fcpp\u002Fdynamic-cast-rtti","02.cpp\u002F80.dynamic-cast-rtti",{"title":2037,"path":2038,"stem":2039},"Шаблони функцій: узагальнене програмування","\u002Fcpp\u002Ffunction-templates","02.cpp\u002F81.function-templates",{"title":2041,"path":2042,"stem":2043},"Шаблони класів та NTTP-параметри","\u002Fcpp\u002Fclass-templates-nttp","02.cpp\u002F82.class-templates-nttp",{"title":2045,"path":2046,"stem":2047},"Повна (явна) спеціалізація шаблонів","\u002Fcpp\u002Ffull-template-specialization","02.cpp\u002F83.full-template-specialization",{"title":2049,"path":2050,"stem":2051},"Часткова спеціалізація шаблонів класів","\u002Fcpp\u002Fpartial-template-specialization","02.cpp\u002F84.partial-template-specialization",{"title":2053,"path":2054,"stem":2055},"Механізм винятків: try, catch, throw та розкручування стеку","\u002Fcpp\u002Fexceptions-basics","02.cpp\u002F85.exceptions-basics",{"title":2057,"path":2058,"stem":2059},"Ієрархія std::exception та власні класи винятків","\u002Fcpp\u002Fexception-hierarchy-custom","02.cpp\u002F86.exception-hierarchy-custom",{"title":2061,"path":2062,"stem":2063},"Повторне генерування винятків та функціональний try-блок","\u002Fcpp\u002Frethrow-function-try","02.cpp\u002F87.rethrow-function-try",{"title":2065,"path":2066,"stem":2067},"Безпека винятків, noexcept та деструктори","\u002Fcpp\u002Fsafety-noexcept","02.cpp\u002F88.safety-noexcept",{"title":2069,"path":2070,"stem":2071},"Стаття 89 — Власні структури даних: стек та черга","\u002Fcpp\u002Fcustom-stack-queue","02.cpp\u002F89.custom-stack-queue",{"title":2073,"path":2074,"stem":2075},"Стаття 90 — Власні структури даних: однозв'язаний список","\u002Fcpp\u002Flinked-list","02.cpp\u002F90.linked-list",{"title":2077,"path":2078,"stem":2079},"Стаття 91 — Власні структури даних: бінарне дерево пошуку","\u002Fcpp\u002Fbinary-search-tree","02.cpp\u002F91.binary-search-tree",{"title":2081,"path":2082,"stem":2083},"Стаття 92 — Власні структури даних: хеш-таблиця","\u002Fcpp\u002Fhash-table","02.cpp\u002F92.hash-table",{"title":2085,"icon":2086,"path":2087,"stem":2088,"children":2089,"page":59},"JavaScript","i-devicon-javascript","\u002Fjavascript","03.javascript",[2090,2116,2170,2192,2496,2534,2552],{"title":2091,"icon":2092,"path":2093,"stem":2094,"children":2095,"page":59},"Events","i-lucide-mouse-pointer-click","\u002Fjavascript\u002Fevents","03.javascript\u002F01.events",[2096,2100,2104,2108,2112],{"title":2097,"path":2098,"stem":2099},"Вступ до подій браузера","\u002Fjavascript\u002Fevents\u002Fintro","03.javascript\u002F01.events\u002F01.intro",{"title":2101,"path":2102,"stem":2103},"Бульбашковий механізм (Bubbling) та занурення (Capturing)","\u002Fjavascript\u002Fevents\u002Fbubbling-capturing","03.javascript\u002F01.events\u002F02.bubbling-capturing",{"title":2105,"path":2106,"stem":2107},"Делегування подій (Event Delegation)","\u002Fjavascript\u002Fevents\u002Fdelegate-events","03.javascript\u002F01.events\u002F03.delegate-events",{"title":2109,"path":2110,"stem":2111},"Типові дії браузера та preventDefault()","\u002Fjavascript\u002Fevents\u002Fprevent-default","03.javascript\u002F01.events\u002F04.prevent-default",{"title":2113,"path":2114,"stem":2115},"Запуск користувацьких подій (Custom Events)","\u002Fjavascript\u002Fevents\u002Fcustom-events","03.javascript\u002F01.events\u002F05.custom-events",{"title":2117,"icon":2118,"path":2119,"stem":2120,"children":2121,"page":59},"Network","i-lucide-globe","\u002Fjavascript\u002Fnetwork","03.javascript\u002F02.network",[2122,2126,2130,2134,2138,2142,2146,2150,2154,2158,2162,2166],{"title":2123,"path":2124,"stem":2125},"Fetch API - Сучасний підхід до HTTP-запитів","\u002Fjavascript\u002Fnetwork\u002F01-fetch-api","03.javascript\u002F02.network\u002F01-fetch-api",{"title":2127,"path":2128,"stem":2129},"FormData - Робота з формами та файлами","\u002Fjavascript\u002Fnetwork\u002F02-formdata","03.javascript\u002F02.network\u002F02-formdata",{"title":2131,"path":2132,"stem":2133},"Відстеження прогресу завантаження","\u002Fjavascript\u002Fnetwork\u002F03-download-progress","03.javascript\u002F02.network\u002F03-download-progress",{"title":2135,"path":2136,"stem":2137},"Переривання fetch-запитів","\u002Fjavascript\u002Fnetwork\u002F04-abort-requests","03.javascript\u002F02.network\u002F04-abort-requests",{"title":2139,"path":2140,"stem":2141},"CORS - Запити між різними джерелами","\u002Fjavascript\u002Fnetwork\u002F05-cors","03.javascript\u002F02.network\u002F05-cors",{"title":2143,"path":2144,"stem":2145},"Fetch API - Повний довідник опцій","\u002Fjavascript\u002Fnetwork\u002F06-fetch-options","03.javascript\u002F02.network\u002F06-fetch-options",{"title":2147,"path":2148,"stem":2149},"URL Objects - Робота з посиланнями","\u002Fjavascript\u002Fnetwork\u002F07-url-objects","03.javascript\u002F02.network\u002F07-url-objects",{"title":2151,"path":2152,"stem":2153},"XMLHttpRequest - AJAX та низькорівневі запити","\u002Fjavascript\u002Fnetwork\u002F08-xmlhttprequest","03.javascript\u002F02.network\u002F08-xmlhttprequest",{"title":2155,"path":2156,"stem":2157},"Відновлюване завантаження файлів","\u002Fjavascript\u002Fnetwork\u002F09-resumable-upload","03.javascript\u002F02.network\u002F09-resumable-upload",{"title":2159,"path":2160,"stem":2161},"Cookies, document.cookie та світ після \"Cookiepocalypse\"","\u002Fjavascript\u002Fnetwork\u002F10-cookies","03.javascript\u002F02.network\u002F10-cookies",{"title":2163,"path":2164,"stem":2165},"js-cookie: Керування Cookies без Болю","\u002Fjavascript\u002Fnetwork\u002F11-js-cookie","03.javascript\u002F02.network\u002F11-js-cookie",{"title":2167,"path":2168,"stem":2169},"Axios: Потужний HTTP-клієнт для JavaScript","\u002Fjavascript\u002Fnetwork\u002F12-axios","03.javascript\u002F02.network\u002F12-axios",{"title":2171,"icon":2172,"path":2173,"stem":2174,"children":2175,"page":59},"Bom","i-lucide-monitor","\u002Fjavascript\u002Fbom","03.javascript\u002F03.bom",[2176,2180,2184,2188],{"title":2177,"path":2178,"stem":2179},"LocalStorage, SessionStorage та patterns збереження даних","\u002Fjavascript\u002Fbom\u002F01-localstorage","03.javascript\u002F03.bom\u002F01-localstorage",{"title":2181,"path":2182,"stem":2183},"Location Object - Керування адресою сторінки","\u002Fjavascript\u002Fbom\u002F02-location-object","03.javascript\u002F03.bom\u002F02-location-object",{"title":2185,"path":2186,"stem":2187},"History API - Керування історією браузера","\u002Fjavascript\u002Fbom\u002F03-history-api","03.javascript\u002F03.bom\u002F03-history-api",{"title":2189,"path":2190,"stem":2191},"Navigator Object - Ідентифікація та Можливості Пристрою","\u002Fjavascript\u002Fbom\u002F04-navigator-object","03.javascript\u002F03.bom\u002F04-navigator-object",{"title":2193,"icon":2194,"path":2195,"stem":2196,"children":2197},"React","i-devicon-react","\u002Fjavascript\u002Freact","03.javascript\u002F04.react\u002Findex",[2198,2199,2203,2207,2211,2215,2278,2313,2465],{"title":2193,"path":2195,"stem":2196},{"title":2200,"path":2201,"stem":2202},"Робота з Формами в React","\u002Fjavascript\u002Freact\u002Freact-forms","03.javascript\u002F04.react\u002F01.react-forms",{"title":2204,"path":2205,"stem":2206},"React Hook Form: Професійна Робота з Формами","\u002Fjavascript\u002Freact\u002Freact-hook-form","03.javascript\u002F04.react\u002F02.react-hook-form",{"title":2208,"path":2209,"stem":2210},"React Hook Form: Глибоке Розуміння Архітектури та Оптимізації","\u002Fjavascript\u002Freact\u002Freact-hook-form-new","03.javascript\u002F04.react\u002F02.react-hook-form-new",{"title":2212,"path":2213,"stem":2214},"Axios та React: Професійна Архітектура Запитів","\u002Fjavascript\u002Freact\u002Fdata-fetching-axios","03.javascript\u002F04.react\u002F03.data-fetching-axios",{"title":2216,"icon":132,"path":2217,"stem":2218,"children":2219},"Tanstack Query","\u002Fjavascript\u002Freact\u002Ftanstack-query","03.javascript\u002F04.react\u002F04.tanstack-query\u002Findex",[2220,2222,2226,2230,2234,2238,2242,2246,2250,2254,2258,2262,2266,2270,2274],{"title":2221,"path":2217,"stem":2218},"TanStack Query: Майстерність Керування Станом Сервера",{"title":2223,"path":2224,"stem":2225},"Парадигма Server State: Чому useEffect недостатньо","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-state-paradigm","03.javascript\u002F04.react\u002F04.tanstack-query\u002F01.server-state-paradigm",{"title":2227,"path":2228,"stem":2229},"Встановлення та Налаштування: Фундамент","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Finstallation-and-devtools","03.javascript\u002F04.react\u002F04.tanstack-query\u002F02.installation-and-devtools",{"title":2231,"path":2232,"stem":2233},"Основи Запитів та Магія Ключів","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fquery-basics-and-keys","03.javascript\u002F04.react\u002F04.tanstack-query\u002F03.query-basics-and-keys",{"title":2235,"path":2236,"stem":2237},"Синхронізація Даних: Життєвий Цикл Запиту","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fdata-synchronization","03.javascript\u002F04.react\u002F04.tanstack-query\u002F04.data-synchronization",{"title":2239,"path":2240,"stem":2241},"Мутації та Інвалідація: Зміна Даних","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fmutations-and-invalidation","03.javascript\u002F04.react\u002F04.tanstack-query\u002F05.mutations-and-invalidation",{"title":2243,"path":2244,"stem":2245},"Оптимістичні Оновлення: Швидше за Світло","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Foptimistic-updates","03.javascript\u002F04.react\u002F04.tanstack-query\u002F06.optimistic-updates",{"title":2247,"path":2248,"stem":2249},"Пагінація та Infinite Scroll","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fpagination-and-load-more","03.javascript\u002F04.react\u002F04.tanstack-query\u002F07.pagination-and-load-more",{"title":2251,"path":2252,"stem":2253},"Просунуті Патерни та Оптимізація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F04.tanstack-query\u002F08.advanced-patterns",{"title":2255,"path":2256,"stem":2257},"Архітектура та Best Practices","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Farchitecture-and-best-practices","03.javascript\u002F04.react\u002F04.tanstack-query\u002F09.architecture-and-best-practices",{"title":2259,"path":2260,"stem":2261},"Server-Side Rendering (SSR) та Гідратація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-side-rendering","03.javascript\u002F04.react\u002F04.tanstack-query\u002F10.server-side-rendering",{"title":2263,"path":2264,"stem":2265},"Стратегії Тестування","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Ftesting-strategies","03.javascript\u002F04.react\u002F04.tanstack-query\u002F11.testing-strategies",{"title":2267,"path":2268,"stem":2269},"Аутентифікація та Обробка Помилок","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fauthentication-and-errors","03.javascript\u002F04.react\u002F04.tanstack-query\u002F12.authentication-and-errors",{"title":2271,"path":2272,"stem":2273},"React Suspense та Майбутнє","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Freact-suspense","03.javascript\u002F04.react\u002F04.tanstack-query\u002F13.react-suspense",{"title":2275,"path":2276,"stem":2277},"Глибоке Занурення в Продуктивність","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fperformance-deep-dive","03.javascript\u002F04.react\u002F04.tanstack-query\u002F14.performance-deep-dive",{"title":2279,"icon":2194,"path":2280,"stem":2281,"children":2282},"React Router","\u002Fjavascript\u002Freact\u002Freact-router","03.javascript\u002F04.react\u002F05.react-router\u002Findex",[2283,2285,2289,2293,2297,2301,2305,2309],{"title":2284,"path":2280,"stem":2281},"React Router: Навігаційна система сучасного вебу",{"title":2286,"path":2287,"stem":2288},"Налаштування та Базовий Роутинг","\u002Fjavascript\u002Freact\u002Freact-router\u002Fsetup-and-basic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F01.setup-and-basic-routing",{"title":2290,"path":2291,"stem":2292},"Динамічна Навігація","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnavigation-and-links","03.javascript\u002F04.react\u002F05.react-router\u002F02.navigation-and-links",{"title":2294,"path":2295,"stem":2296},"Вкладені Маршрути та Макети","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnested-routes-and-layouts","03.javascript\u002F04.react\u002F05.react-router\u002F03.nested-routes-and-layouts",{"title":2298,"path":2299,"stem":2300},"Динамічні Маршрути та Параметри","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdynamic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F04.dynamic-routing",{"title":2302,"path":2303,"stem":2304},"Data APIs: Loaders та Actions","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdata-loading","03.javascript\u002F04.react\u002F05.react-router\u002F05.data-loading",{"title":2306,"path":2307,"stem":2308},"Просунуті Патерни","\u002Fjavascript\u002Freact\u002Freact-router\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F05.react-router\u002F06.advanced-patterns",{"title":2310,"path":2311,"stem":2312},"Legacy Routing: Компонентний підхід","\u002Fjavascript\u002Freact\u002Freact-router\u002Flegacy-routing","03.javascript\u002F04.react\u002F05.react-router\u002F07.legacy-routing",{"title":2314,"icon":132,"path":2315,"stem":2316,"children":2317},"Redux","\u002Fjavascript\u002Freact\u002Fredux","03.javascript\u002F04.react\u002F06.redux\u002Findex",[2318,2320,2336,2365,2374,2395,2411,2440],{"title":2319,"path":2315,"stem":2316},"Redux: Еволюція управління станом",{"title":14,"icon":15,"path":2321,"stem":2322,"children":2323,"page":59},"\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals",[2324,2328,2332],{"title":2325,"path":2326,"stem":2327},"Вступ до State Management","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fintro-state-management","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F01.intro-state-management",{"title":2329,"path":2330,"stem":2331},"Філософія Redux та Три Принципи","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fredux-philosophy","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F02.redux-philosophy",{"title":2333,"path":2334,"stem":2335},"Чисті функції та Іммутабельність","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fpure-functions-immutability","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F03.pure-functions-immutability",{"title":2337,"icon":132,"path":2338,"stem":2339,"children":2340,"page":59},"Classic Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux",[2341,2345,2349,2353,2357,2361],{"title":2342,"path":2343,"stem":2344},"Створення Store (Classic Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fstore-setup","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F01.store-setup",{"title":2346,"path":2347,"stem":2348},"Actions, Constants та Action Creators","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Factions-constants","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F02.actions-constants",{"title":2350,"path":2351,"stem":2352},"Логіка Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freducers","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F03.reducers",{"title":2354,"path":2355,"stem":2356},"Комбінування Reducers (Root Reducer)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fdata-flow","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F04.data-flow",{"title":2358,"path":2359,"stem":2360},"Підключення до React (React-Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freact-redux-connection","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F05.react-redux-connection",{"title":2362,"path":2363,"stem":2364},"Middleware та Асинхронність (Redux Thunk)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fmiddleware-thunk","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F06.middleware-thunk",{"title":2366,"icon":132,"path":2367,"stem":2368,"children":2369,"page":59},"Transition To Rtk","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk",[2370],{"title":2371,"path":2372,"stem":2373},"Проблеми класичного Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk\u002Fproblems-with-classic","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk\u002F01.problems-with-classic",{"title":2375,"icon":132,"path":2376,"stem":2377,"children":2378,"page":59},"Redux Toolkit","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit",[2379,2383,2387,2391],{"title":2380,"path":2381,"stem":2382},"Налаштування Store з configureStore","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fconfigure-store","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F01.configure-store",{"title":2384,"path":2385,"stem":2386},"createSlice: Революція в Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fcreate-slice","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F02.create-slice",{"title":2388,"path":2389,"stem":2390},"Асинхронність з createAsyncThunk","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fasync-thunks","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F03.async-thunks",{"title":2392,"path":2393,"stem":2394},"04. Entity Adapter: Керування нормалізованим станом","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fentity-adapter","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F04.entity-adapter",{"title":2396,"icon":92,"path":2397,"stem":2398,"children":2399,"page":59},"Advanced","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced",[2400,2404,2408],{"title":2401,"path":2402,"stem":2403},"Мемоізація та Селектори: Повний Гайд по Reselect","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Fselectors-reselect","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F01.selectors-reselect",{"title":2405,"path":2406,"stem":2407},"RTK Query: Архітектура Серверного Кешу","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Frtk-query-intro","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F02.rtk-query-intro",{"title":2255,"path":2409,"stem":2410},"\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Farchitecture-best-practices","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F03.architecture-best-practices",{"title":2412,"icon":132,"path":2413,"stem":2414,"children":2415,"page":59},"Project Kanban","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban",[2416,2420,2424,2428,2432,2436],{"title":2417,"path":2418,"stem":2419},"Проєкт: Kanban Board (Trello Clone)","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fproject-overview","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F01.project-overview",{"title":2421,"path":2422,"stem":2423},"Налаштування та Типізація","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fsetup-and-types","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F02.setup-and-types",{"title":2425,"path":2426,"stem":2427},"Board Slice: Серце Дошки","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fboard-slice","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F03.board-slice",{"title":2429,"path":2430,"stem":2431},"Логіка Drag & Drop","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fdrag-and-drop-logic","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F04.drag-and-drop-logic",{"title":2433,"path":2434,"stem":2435},"Інтеграція з RTK Query","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Frtk-query-integration","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F05.rtk-query-integration",{"title":2437,"path":2438,"stem":2439},"Optimistic Updates","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Foptimistic-updates","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F06.optimistic-updates",{"title":2441,"icon":132,"path":2442,"stem":2443,"children":2444,"page":59},"Testing","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting","03.javascript\u002F04.react\u002F06.redux\u002F07.testing",[2445,2449,2453,2457,2461],{"title":2446,"path":2447,"stem":2448},"Тестування Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Fintro-testing","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F01.intro-testing",{"title":2450,"path":2451,"stem":2452},"Тестування Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-reducers","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F02.testing-reducers",{"title":2454,"path":2455,"stem":2456},"Тестування Селекторів","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-selectors","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F03.testing-selectors",{"title":2458,"path":2459,"stem":2460},"Тестування Компонентів (Integration)","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-components","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F04.testing-components",{"title":2462,"path":2463,"stem":2464},"Тестування Async Thunks","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-thunks","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F05.testing-thunks",{"title":2466,"icon":132,"path":2467,"stem":2468,"children":2469},"Ui Libraries","\u002Fjavascript\u002Freact\u002Fui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002Findex",[2470,2472,2476,2480,2484,2488,2492],{"title":2471,"path":2467,"stem":2468},"UI Бібліотеки в React",{"title":2473,"path":2474,"stem":2475},"Вступ до UI Бібліотек: Навіщо Винаходити Велосипед Двічі?","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fintroduction-to-ui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002F01.introduction-to-ui-libraries",{"title":2477,"path":2478,"stem":2479},"Філософія shadcn\u002Fui: \"Not a Component Library\"","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-philosophy","03.javascript\u002F04.react\u002F07.ui-libraries\u002F02.shadcn-philosophy",{"title":2481,"path":2482,"stem":2483},"Установка та Налаштування shadcn\u002Fui","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-installation","03.javascript\u002F04.react\u002F07.ui-libraries\u002F03.shadcn-installation",{"title":2485,"path":2486,"stem":2487},"Базові Компоненти shadcn\u002Fui: Фундамент Інтерфейсу","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-basics","03.javascript\u002F04.react\u002F07.ui-libraries\u002F04.shadcn-components-basics",{"title":2489,"path":2490,"stem":2491},"Компоненти Форм: Побудова Інтерактивних Form","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-forms","03.javascript\u002F04.react\u002F07.ui-libraries\u002F05.shadcn-components-forms",{"title":2493,"path":2494,"stem":2495},"Складні Компоненти: Dialog, Dropdown, Table та Command","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-advanced","03.javascript\u002F04.react\u002F07.ui-libraries\u002F06.shadcn-components-advanced",{"title":2497,"icon":2498,"path":2499,"stem":2500,"children":2501,"page":59},"TypeScript","i-devicon-typescript","\u002Fjavascript\u002Ftypescript","03.javascript\u002F05.typescript",[2502,2506,2510,2514,2518,2522,2526,2530],{"title":2503,"path":2504,"stem":2505},"TypeScript: Броня для вашого коду","\u002Fjavascript\u002Ftypescript\u002Fintro-and-basic-types","03.javascript\u002F05.typescript\u002F01.intro-and-basic-types",{"title":2507,"path":2508,"stem":2509},"Майстерність Моделювання Даних: Інтерфейси та Просунуті Типи","\u002Fjavascript\u002Ftypescript\u002Finterfaces-and-advanced-types","03.javascript\u002F05.typescript\u002F02.interfaces-and-advanced-types",{"title":2511,"path":2512,"stem":2513},"Алхімія Типів: Generics та Utility Types","\u002Fjavascript\u002Ftypescript\u002Fgenerics-and-utilities","03.javascript\u002F05.typescript\u002F03.generics-and-utilities",{"title":2515,"path":2516,"stem":2517},"Архітектура та Шаблони: Класи в TypeScript","\u002Fjavascript\u002Ftypescript\u002Fclasses-and-oop","03.javascript\u002F05.typescript\u002F04.classes-and-oop",{"title":2519,"path":2520,"stem":2521},"Продакшн та Екосистема: Advanced Config & Workflow","\u002Fjavascript\u002Ftypescript\u002Fadvanced-patterns-and-config","03.javascript\u002F05.typescript\u002F05.advanced-patterns-and-config",{"title":2523,"path":2524,"stem":2525},"TypeScript у світі React","\u002Fjavascript\u002Ftypescript\u002Freact-basics","03.javascript\u002F05.typescript\u002F06.react-basics",{"title":2527,"path":2528,"stem":2529},"React + TypeScript: Продвинуті патерни","\u002Fjavascript\u002Ftypescript\u002Freact-advanced","03.javascript\u002F05.typescript\u002F07.react-advanced",{"title":2531,"path":2532,"stem":2533},"React + TypeScript: Екосистема та бібліотеки","\u002Fjavascript\u002Ftypescript\u002Freact-ecosystem","03.javascript\u002F05.typescript\u002F08.react-ecosystem",{"title":2535,"icon":2536,"path":2537,"stem":2538,"children":2539,"page":59},"Асинхронність","i-lucide-refresh-cw","\u002Fjavascript\u002Fasync-programming","03.javascript\u002F06.async-programming",[2540,2544,2548],{"title":2541,"path":2542,"stem":2543},"Event Loop: серце асинхронності в JavaScript","\u002Fjavascript\u002Fasync-programming\u002Fevent-loop-fundamentals","03.javascript\u002F06.async-programming\u002F01.event-loop-fundamentals",{"title":2545,"path":2546,"stem":2547},"Task Queue та Microtask Queue: пріоритети виконання","\u002Fjavascript\u002Fasync-programming\u002Ftask-queues","03.javascript\u002F06.async-programming\u002F02.task-queues",{"title":2549,"path":2550,"stem":2551},"Асинхронні патерни: від Callbacks до Async\u002FAwait","\u002Fjavascript\u002Fasync-programming\u002Fasync-patterns","03.javascript\u002F06.async-programming\u002F03.async-patterns",{"title":2553,"path":2554,"stem":2555},"Atomic Design","\u002Fjavascript\u002Fatomic-design","03.javascript\u002F2.atomic-design",{"title":2557,"icon":2558,"path":2559,"stem":2560,"children":2561,"page":59},"Java","i-devicon-java","\u002Fjava","04.java",[2562,2565,2568,2572,2576,2580,2584],{"title":162,"path":2563,"stem":2564},"\u002Fjava\u002Fdata-mapper-part1","04.java\u002F01.data-mapper-part1",{"title":166,"path":2566,"stem":2567},"\u002Fjava\u002Fdata-mapper-part2","04.java\u002F02.data-mapper-part2",{"title":2569,"path":2570,"stem":2571},"Service Layer: Організація бізнес-логіки","\u002Fjava\u002Fservice-layer","04.java\u002F03.service-layer",{"title":2573,"path":2574,"stem":2575},"Rich Domain Model та State Pattern","\u002Fjava\u002Frich-domain-model","04.java\u002F04.rich-domain-model",{"title":2577,"path":2578,"stem":2579},"Патерни для складної бізнес-логіки","\u002Fjava\u002Fbusiness-logic-patterns","04.java\u002F05.business-logic-patterns",{"title":2581,"path":2582,"stem":2583},"Обробка помилок та валідація","\u002Fjava\u002Ferror-handling-validation","04.java\u002F06.error-handling-validation",{"title":2585,"path":2586,"stem":2587,"children":2588,"page":59},"Проектування баз даних","\u002Fjava\u002Fpr2","04.java\u002Fpr2",[2589,2593,2597,2601,2605,2609,2613,2617,2621,2625,2629,2633,2637,2641,2645,2649,2653,2657,2661,2665,2669,2673,2677,2681,2685,2689,2693,2697,2701,2705,2709,2713,2717,2721,2725,2729,2733],{"title":2590,"path":2591,"stem":2592},"Концептуальне моделювання: Мистецтво розуміння предметної області","\u002Fjava\u002Fpr2\u002Fconceptual-modeling","04.java\u002Fpr2\u002F01.conceptual-modeling",{"title":2594,"path":2595,"stem":2596},"Логічне моделювання: Від бізнес-ідей до структур даних","\u002Fjava\u002Fpr2\u002Flogical-modeling","04.java\u002Fpr2\u002F02.logical-modeling",{"title":2598,"path":2599,"stem":2600},"Нормалізація: Гігієна даних та боротьба з аномаліями","\u002Fjava\u002Fpr2\u002Fnormalization","04.java\u002Fpr2\u002F03.normalization",{"title":2602,"path":2603,"stem":2604},"Фізична схема: Від абстракції до DDL","\u002Fjava\u002Fpr2\u002Fphysical-schema","04.java\u002Fpr2\u002F04.physical-schema",{"title":2606,"path":2607,"stem":2608},"Архітектурна класифікація таблиць","\u002Fjava\u002Fpr2\u002Ftable-classification","04.java\u002Fpr2\u002F05.table-classification",{"title":2610,"path":2611,"stem":2612},"Database Migrations: Версіонування схеми з Flyway","\u002Fjava\u002Fpr2\u002Fdatabase-migrations","04.java\u002Fpr2\u002F06.database-migrations",{"title":2614,"path":2615,"stem":2616},"А що, якби це була не реляційна БД?","\u002Fjava\u002Fpr2\u002Fbeyond-relational","04.java\u002Fpr2\u002F07.beyond-relational",{"title":2618,"path":2619,"stem":2620},"Object-Relational Impedance Mismatch: Два світи, що не хочуть дружити","\u002Fjava\u002Fpr2\u002Fimpedance-mismatch","04.java\u002Fpr2\u002F09.impedance-mismatch",{"title":2622,"path":2623,"stem":2624},"JDBC: Перший контакт із базою даних","\u002Fjava\u002Fpr2\u002Fjdbc-fundamentals","04.java\u002Fpr2\u002F10.jdbc-fundamentals",{"title":2626,"path":2627,"stem":2628},"Якість коду: Spotless, SpotBugs та SonarQube","\u002Fjava\u002Fpr2\u002F10a.code-quality","04.java\u002Fpr2\u002F10a.code-quality",{"title":2630,"path":2631,"stem":2632},"Connection Pool: Патерн Object Pool для JDBC-з'єднань","\u002Fjava\u002Fpr2\u002Fconnection-pool","04.java\u002Fpr2\u002F11.connection-pool",{"title":2634,"path":2635,"stem":2636},"Row Data Gateway: Об'єкт як обгортка рядка таблиці","\u002Fjava\u002Fpr2\u002Frow-data-gateway","04.java\u002Fpr2\u002F12.row-data-gateway",{"title":2638,"path":2639,"stem":2640},"Table Data Gateway: Фасад таблиці як архітектурний відступ","\u002Fjava\u002Fpr2\u002Ftable-data-gateway","04.java\u002Fpr2\u002F13.table-data-gateway",{"title":2642,"path":2643,"stem":2644},"Repository + Data Mapper: Правильна шарова архітектура з JDBC","\u002Fjava\u002Fpr2\u002Frepository-data-mapper","04.java\u002Fpr2\u002F14.repository-data-mapper",{"title":2646,"path":2647,"stem":2648},"Identity Map: Кешування сутностей у рамках сесії","\u002Fjava\u002Fpr2\u002Fidentity-map","04.java\u002Fpr2\u002F15.identity-map",{"title":2650,"path":2651,"stem":2652},"Unit of Work: Відстеження змін і координація JDBC-транзакцій","\u002Fjava\u002Fpr2\u002Funit-of-work","04.java\u002Fpr2\u002F16.unit-of-work",{"title":2654,"path":2655,"stem":2656},"Strategy: Замінювані SQL-стратегії для підтримки різних СУБД","\u002Fjava\u002Fpr2\u002Fstrategy-sql","04.java\u002Fpr2\u002F17.strategy-sql",{"title":2658,"path":2659,"stem":2660},"Proxy: Lazy Loading для One-To-Many колекцій","\u002Fjava\u002Fpr2\u002Fproxy-lazy-loading","04.java\u002Fpr2\u002F18.proxy-lazy-loading",{"title":2662,"path":2663,"stem":2664},"Generic Repository через Java Reflection: анотації та динамічний SQL","\u002Fjava\u002Fpr2\u002Fgeneric-repository-reflection","04.java\u002Fpr2\u002F19.generic-repository-reflection",{"title":2666,"path":2667,"stem":2668},"Specification Pattern: Композиція бізнес-правил для складних запитів","\u002Fjava\u002Fpr2\u002Fspecification-pattern","04.java\u002Fpr2\u002F20.specification-pattern",{"title":2670,"path":2671,"stem":2672},"Розширені можливості Specification Pattern: підзапити, агрегації та гібридний підхід","\u002Fjava\u002Fpr2\u002F20a.advanced-specifications","04.java\u002Fpr2\u002F20a.advanced-specifications",{"title":2674,"path":2675,"stem":2676},"Асинхронність у JDBC: Від блокуючих викликів до CompletableFuture","\u002Fjava\u002Fpr2\u002Fasynchronous-jdbc","04.java\u002Fpr2\u002F21.asynchronous-jdbc",{"title":2678,"path":2679,"stem":2680},"Інтеграційне тестування JDBC-репозиторіїв: Embedded H2 та патерн AAA","\u002Fjava\u002Fpr2\u002Fintegration-testing-h2","04.java\u002Fpr2\u002F22.integration-testing-h2",{"title":2682,"path":2683,"stem":2684},"Testcontainers: Тестування з реальною PostgreSQL у Docker-контейнерах","\u002Fjava\u002Fpr2\u002Fintegration-testing-testcontainers","04.java\u002Fpr2\u002F23.integration-testing-testcontainers",{"title":2686,"path":2687,"stem":2688},"Google Guice: Впровадження залежностей у JavaFX-проєкті","\u002Fjava\u002Fpr2\u002Fdependency-injection-guice","04.java\u002Fpr2\u002F24.dependency-injection-guice",{"title":2690,"path":2691,"stem":2692},"JavaFX: Основи побудови графічних інтерфейсів","\u002Fjava\u002Fpr2\u002Fjavafx-fundamentals","04.java\u002Fpr2\u002F25.javafx-fundamentals",{"title":2694,"path":2695,"stem":2696},"Properties та Bindings: Реактивність у JavaFX","\u002Fjava\u002Fpr2\u002Fjavafx-properties-bindings","04.java\u002Fpr2\u002F26.javafx-properties-bindings",{"title":2698,"path":2699,"stem":2700},"MVC vs MVP vs MVVM: Еволюція архітектурних патернів UI","\u002Fjava\u002Fpr2\u002Fui-architecture-patterns","04.java\u002Fpr2\u002F27.ui-architecture-patterns",{"title":2702,"path":2703,"stem":2704},"MVVM на практиці: Побудова ViewModel","\u002Fjava\u002Fpr2\u002Fmvvm-viewmodel-implementation","04.java\u002Fpr2\u002F28.mvvm-viewmodel-implementation",{"title":2706,"path":2707,"stem":2708},"View та Controller: Зв'язування з ViewModel через FXML","\u002Fjava\u002Fpr2\u002Fmvvm-view-controller","04.java\u002Fpr2\u002F29.mvvm-view-controller",{"title":2710,"path":2711,"stem":2712},"Інтеграція MVVM з Guice: Автоматична ін'єкція залежностей","\u002Fjava\u002Fpr2\u002Fmvvm-guice-integration","04.java\u002Fpr2\u002F30.mvvm-guice-integration",{"title":2714,"path":2715,"stem":2716},"Валідація та обробка помилок у MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-validation-error-handling","04.java\u002Fpr2\u002F31.mvvm-validation-error-handling",{"title":2718,"path":2719,"stem":2720},"Навігація та управління екранами у JavaFX MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-navigation-screen-management","04.java\u002Fpr2\u002F32.mvvm-navigation-screen-management",{"title":2722,"path":2723,"stem":2724},"Тестування JavaFX MVVM-додатків","\u002Fjava\u002Fpr2\u002Fmvvm-testing","04.java\u002Fpr2\u002F33.mvvm-testing",{"title":2726,"path":2727,"stem":2728},"Стилізація та теми у JavaFX: CSS та User Experience","\u002Fjava\u002Fpr2\u002Fjavafx-styling-themes","04.java\u002Fpr2\u002F34.javafx-styling-themes",{"title":2730,"path":2731,"stem":2732},"AtlantaFX: Сучасні теми для JavaFX додатків","\u002Fjava\u002Fpr2\u002Fatlantafx-modern-themes","04.java\u002Fpr2\u002F35.atlantafx-modern-themes",{"title":2734,"path":2735,"stem":2736},"Пакування та розповсюдження JavaFX-додатків","\u002Fjava\u002Fpr2\u002Fjar-packaging-distribution","04.java\u002Fpr2\u002F36.jar-packaging-distribution",{"title":2738,"icon":2739,"path":2740,"stem":2741,"children":2742,"page":59},"Python","i-devicon-python","\u002Fpython","05.python",[2743,2747,2750,2754,2758,2762,2766,2770,2774,2778,2782,2786,2790,2794,2798,2859],{"title":2744,"path":2745,"stem":2746},"Модулі, Пакети та Віртуальні Середовища","\u002Fpython\u002Fmodules-packages-venv","05.python\u002F00.modules-packages-venv",{"title":71,"path":2748,"stem":2749},"\u002Fpython\u002Fclasses-objects","05.python\u002F01.classes-objects",{"title":2751,"path":2752,"stem":2753},"Інкапсуляція, Керування Доступом та Властивості","\u002Fpython\u002Fencapsulation","05.python\u002F02.encapsulation",{"title":2755,"path":2756,"stem":2757},"Наслідування, MRO та суперсила super()","\u002Fpython\u002Finheritance-mro","05.python\u002F03.inheritance-mro",{"title":2759,"path":2760,"stem":2761},"Абстракція — ABC проти Статичних Протоколів (PEP 544)","\u002Fpython\u002Fabstraction-protocols","05.python\u002F04.abstraction-protocols",{"title":2763,"path":2764,"stem":2765},"Магічні методи (Dunder) та Емуляція протоколів","\u002Fpython\u002Fdunder-methods","05.python\u002F05.dunder-methods",{"title":2767,"path":2768,"stem":2769},"Декоратори та Керування життєвим циклом методів","\u002Fpython\u002Fdecorators-static-class","05.python\u002F06.decorators-static-class",{"title":2771,"path":2772,"stem":2773},"Дескриптори — Магія доступу до атрибутів","\u002Fpython\u002Fdescriptors","05.python\u002F07.descriptors",{"title":2775,"path":2776,"stem":2777},"Метакласи — Динамічне створення класів під капотом CPython","\u002Fpython\u002Fmetaclasses","05.python\u002F08.metaclasses",{"title":2779,"path":2780,"stem":2781},"Dataclasses, NamedTuple та сучасні контейнери Python","\u002Fpython\u002Fmodern-containers","05.python\u002F09.modern-containers",{"title":2783,"path":2784,"stem":2785},"GIL та модель конкурентності CPython — фундамент перед потоками і процесами","\u002Fpython\u002Fgil-concurrency-intro","05.python\u002F11.gil-concurrency-intro",{"title":2787,"path":2788,"stem":2789},"Threading — конкурентність для I\u002FO-bound задач","\u002Fpython\u002Fthreading","05.python\u002F12.threading",{"title":2791,"path":2792,"stem":2793},"Multiprocessing — справжній паралелізм для CPU-bound задач","\u002Fpython\u002Fmultiprocessing","05.python\u002F13.multiprocessing",{"title":2795,"path":2796,"stem":2797},"asyncio — кооперативна конкурентність та event loop","\u002Fpython\u002Fasyncio","05.python\u002F14.asyncio",{"title":2799,"icon":92,"path":2800,"stem":2801,"children":2802,"page":59},"FastAPI","\u002Fpython\u002Ffastapi","05.python\u002Ffastapi",[2803,2807,2811,2815,2819,2823,2827,2831,2835,2839,2843,2847,2851,2855],{"title":2804,"path":2805,"stem":2806},"Глибокий Typing та Pydantic v2 — від анотацій до валідації","\u002Fpython\u002Ffastapi\u002Ftyping-pydantic","05.python\u002Ffastapi\u002F15.typing-pydantic",{"title":2808,"path":2809,"stem":2810},"WSGI, ASGI та Python Web-екосистема","\u002Fpython\u002Ffastapi\u002Fwsgi-asgi-ecosystem","05.python\u002Ffastapi\u002F16.wsgi-asgi-ecosystem",{"title":2812,"path":2813,"stem":2814},"FastAPI: Перший додаток, Uvicorn та OpenAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-intro","05.python\u002Ffastapi\u002F17.fastapi-intro",{"title":2816,"path":2817,"stem":2818},"Маршрутизація, параметри запитів та APIRouter","\u002Fpython\u002Ffastapi\u002Ffastapi-routing-params","05.python\u002Ffastapi\u002F18.fastapi-routing-params",{"title":2820,"path":2821,"stem":2822},"Pydantic v2 у FastAPI — схеми, валідація та серіалізація","\u002Fpython\u002Ffastapi\u002Ffastapi-pydantic-schemas","05.python\u002Ffastapi\u002F19.fastapi-pydantic-schemas",{"title":2824,"path":2825,"stem":2826},"Dependency Injection — серце архітектури FastAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-dependency-injection","05.python\u002Ffastapi\u002F20.fastapi-dependency-injection",{"title":2828,"path":2829,"stem":2830},"Обробка помилок, Middleware та CORS у FastAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-errors-middleware","05.python\u002Ffastapi\u002F21.fastapi-errors-middleware",{"title":2832,"path":2833,"stem":2834},"SQLAlchemy 2.0 — ORM, Core та Async Engine","\u002Fpython\u002Ffastapi\u002Fsqlalchemy-orm","05.python\u002Ffastapi\u002F22.sqlalchemy-orm",{"title":2836,"path":2837,"stem":2838},"Alembic — міграції бази даних","\u002Fpython\u002Ffastapi\u002Falembic-migrations","05.python\u002Ffastapi\u002F23.alembic-migrations",{"title":2840,"path":2841,"stem":2842},"Repository Pattern та Clean Architecture у FastAPI","\u002Fpython\u002Ffastapi\u002Ffastapi-repository-clean-arch","05.python\u002Ffastapi\u002F24.fastapi-repository-clean-arch",{"title":2844,"path":2845,"stem":2846},"Автентифікація та авторизація — JWT, OAuth2, RBAC","\u002Fpython\u002Ffastapi\u002Ffastapi-auth-security","05.python\u002Ffastapi\u002F25.fastapi-auth-security",{"title":2848,"path":2849,"stem":2850},"Тестування FastAPI — pytest, httpx, fixtures","\u002Fpython\u002Ffastapi\u002Ffastapi-testing","05.python\u002Ffastapi\u002F26.fastapi-testing",{"title":2852,"path":2853,"stem":2854},"Async на повну — Background Tasks, WebSockets, SSE та Celery","\u002Fpython\u002Ffastapi\u002Ffastapi-async-advanced","05.python\u002Ffastapi\u002F27.fastapi-async-advanced",{"title":2856,"path":2857,"stem":2858},"Deployment — Docker, CI\u002FCD, Monitoring","\u002Fpython\u002Ffastapi\u002Ffastapi-deployment","05.python\u002Ffastapi\u002F28.fastapi-deployment",{"title":2860,"path":2861,"stem":2862},"[object Object]","\u002Fpython\u002Foop-plan","05.python\u002Foop-plan",{"title":2864,"icon":2865,"path":2866,"stem":2867,"children":2868,"page":59},"Бази даних","i-lucide-database","\u002Fdatabases","06.databases",[2869,2899,2922,2959,2988,3006,3040,3052,3061],{"title":2870,"icon":2871,"path":2872,"stem":2873,"children":2874,"page":59},"Intro","i-lucide-play","\u002Fdatabases\u002Fintro","06.databases\u002F01.intro",[2875,2879,2883,2887,2891,2895],{"title":2876,"path":2877,"stem":2878},"Введення в теорію баз даних","\u002Fdatabases\u002Fintro\u002Fintroduction-to-databases","06.databases\u002F01.intro\u002F01.introduction-to-databases",{"title":2880,"path":2881,"stem":2882},"Реляційна модель даних","\u002Fdatabases\u002Fintro\u002Frelational-model-theory","06.databases\u002F01.intro\u002F02.relational-model-theory",{"title":2884,"path":2885,"stem":2886},"ER-моделювання","\u002Fdatabases\u002Fintro\u002Fer-modeling","06.databases\u002F01.intro\u002F03.er-modeling",{"title":2888,"path":2889,"stem":2890},"Логічне проектування БД","\u002Fdatabases\u002Fintro\u002Flogical-schema","06.databases\u002F01.intro\u002F04.logical-schema",{"title":2892,"path":2893,"stem":2894},"Класифікація таблиць","\u002Fdatabases\u002Fintro\u002Ftable-classification","06.databases\u002F01.intro\u002F05.table-classification",{"title":2896,"path":2897,"stem":2898},"PlantUML для баз даних","\u002Fdatabases\u002Fintro\u002Fplantuml-diagrams","06.databases\u002F01.intro\u002F06.plantuml-diagrams",{"title":2900,"icon":2865,"path":2901,"stem":2902,"children":2903,"page":59},"MS SQL Server Start","\u002Fdatabases\u002Fms-sql-server-start","06.databases\u002F02.ms-sql-server-start",[2904,2908,2914,2918],{"title":2905,"path":2906,"stem":2907},"Типи даних у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fdata-types","06.databases\u002F02.ms-sql-server-start\u002F01.data-types",{"title":2909,"path":2910,"stem":2911,"children":2912},"Індекси у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-indexes","06.databases\u002F02.ms-sql-server-start\u002F02.sql-indexes",[2913],{"title":2909,"path":2910,"stem":2911},{"title":2915,"path":2916,"stem":2917},"Системні бази даних MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsystem-databases","06.databases\u002F02.ms-sql-server-start\u002F03.system-databases",{"title":2919,"path":2920,"stem":2921},"Огляд мови SQL та запитів","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-queries-overview","06.databases\u002F02.ms-sql-server-start\u002F04.sql-queries-overview",{"title":2923,"icon":2865,"path":2924,"stem":2925,"children":2926,"page":59},"SQL","\u002Fdatabases\u002Fsql","06.databases\u002F03.sql",[2927,2931,2935,2939,2943,2947,2951,2955],{"title":2928,"path":2929,"stem":2930},"Налаштування демонстраційної бази даних","\u002Fdatabases\u002Fsql\u002Fsample-database-setup","06.databases\u002F03.sql\u002F00.sample-database-setup",{"title":2932,"path":2933,"stem":2934},"DDL - Створення таблиць (CREATE TABLE)","\u002Fdatabases\u002Fsql\u002Fddl-create-table","06.databases\u002F03.sql\u002F01.ddl-create-table",{"title":2936,"path":2937,"stem":2938},"DDL - Зміна та видалення таблиць (ALTER, DROP)","\u002Fdatabases\u002Fsql\u002Fddl-alter-drop-table","06.databases\u002F03.sql\u002F02.ddl-alter-drop-table",{"title":2940,"path":2941,"stem":2942},"SELECT запити - Основи","\u002Fdatabases\u002Fsql\u002Fselect-queries-fundamentals","06.databases\u002F03.sql\u002F03.select-queries-fundamentals",{"title":2944,"path":2945,"stem":2946},"SELECT запити - Розширені можливості","\u002Fdatabases\u002Fsql\u002Fselect-queries-advanced","06.databases\u002F03.sql\u002F04.select-queries-advanced",{"title":2948,"path":2949,"stem":2950},"INSERT запити - Додавання даних","\u002Fdatabases\u002Fsql\u002Finsert-queries","06.databases\u002F03.sql\u002F05.insert-queries",{"title":2952,"path":2953,"stem":2954},"UPDATE та DELETE запити","\u002Fdatabases\u002Fsql\u002Fupdate-delete-queries","06.databases\u002F03.sql\u002F06.update-delete-queries",{"title":2956,"path":2957,"stem":2958},"Транзакції в SQL","\u002Fdatabases\u002Fsql\u002Ftransactions","06.databases\u002F03.sql\u002F07.transactions",{"title":2960,"icon":2865,"path":2961,"stem":2962,"children":2963,"page":59},"Multi Table Databases","\u002Fdatabases\u002Fmulti-table-databases","06.databases\u002F04.multi-table-databases",[2964,2968,2972,2976,2980,2984],{"title":2965,"path":2966,"stem":2967},"Зв'язки та нормалізація БД","\u002Fdatabases\u002Fmulti-table-databases\u002Frelationships-and-normalization","06.databases\u002F04.multi-table-databases\u002F00.relationships-and-normalization",{"title":2969,"path":2970,"stem":2971},"INNER JOIN - З'єднання таблиць","\u002Fdatabases\u002Fmulti-table-databases\u002Finner-join","06.databases\u002F04.multi-table-databases\u002F01.inner-join",{"title":2973,"path":2974,"stem":2975},"OUTER JOINs - LEFT, RIGHT, FULL","\u002Fdatabases\u002Fmulti-table-databases\u002Fouter-joins","06.databases\u002F04.multi-table-databases\u002F02.outer-joins",{"title":2977,"path":2978,"stem":2979},"CROSS та SELF JOINs","\u002Fdatabases\u002Fmulti-table-databases\u002Fcross-self-joins","06.databases\u002F04.multi-table-databases\u002F03.cross-self-joins",{"title":2981,"path":2982,"stem":2983},"Підзапити (Subqueries)","\u002Fdatabases\u002Fmulti-table-databases\u002Fsubqueries","06.databases\u002F04.multi-table-databases\u002F04.subqueries",{"title":2985,"path":2986,"stem":2987},"Агрегації з JOIN","\u002Fdatabases\u002Fmulti-table-databases\u002Faggregations-with-joins","06.databases\u002F04.multi-table-databases\u002F05.aggregations-with-joins",{"title":2989,"icon":2990,"path":2991,"stem":2992,"children":2993,"page":59},"Aggregate Functions","i-lucide-calculator","\u002Fdatabases\u002Faggregate-functions","06.databases\u002F05.aggregate-functions",[2994,2998,3002],{"title":2995,"path":2996,"stem":2997},"Функції агрегування в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fintroduction-aggregate-functions","06.databases\u002F05.aggregate-functions\u002F01.introduction-aggregate-functions",{"title":2999,"path":3000,"stem":3001},"Групування даних в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fgrouping-data","06.databases\u002F05.aggregate-functions\u002F02.grouping-data",{"title":3003,"path":3004,"stem":3005},"Підзапити з агрегатними функціями","\u002Fdatabases\u002Faggregate-functions\u002Fsubqueries-aggregates","06.databases\u002F05.aggregate-functions\u002F03.subqueries-aggregates",{"title":3007,"icon":3008,"path":3009,"stem":3010,"children":3011,"page":59},"Тригери та зберігаємі процедури","i-lucide-database-zap","\u002Fdatabases\u002Ftriggers-stored-procedures","06.databases\u002F07.triggers-stored-procedures",[3012,3016,3020,3024,3028,3032,3036],{"title":3013,"path":3014,"stem":3015},"DML-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fdml-triggers","06.databases\u002F07.triggers-stored-procedures\u002F01.dml-triggers",{"title":3017,"path":3018,"stem":3019},"DDL-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fddl-triggers","06.databases\u002F07.triggers-stored-procedures\u002F02.ddl-triggers",{"title":3021,"path":3022,"stem":3023},"Transact-SQL розширення","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransact-sql-extensions","06.databases\u002F07.triggers-stored-procedures\u002F03.transact-sql-extensions",{"title":3025,"path":3026,"stem":3027},"Транзакції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransactions","06.databases\u002F07.triggers-stored-procedures\u002F04.transactions",{"title":3029,"path":3030,"stem":3031},"Зберігаємі процедури","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fstored-procedures","06.databases\u002F07.triggers-stored-procedures\u002F05.stored-procedures",{"title":3033,"path":3034,"stem":3035},"Користувацькі функції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fuser-defined-functions","06.databases\u002F07.triggers-stored-procedures\u002F06.user-defined-functions",{"title":3037,"path":3038,"stem":3039},"Безпека баз даних","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fsecurity","06.databases\u002F07.triggers-stored-procedures\u002F08.security",{"title":3037,"icon":793,"path":3041,"stem":3042,"children":3043,"page":59},"\u002Fdatabases\u002Fsecurity","06.databases\u002F08.security",[3044,3048],{"title":3045,"path":3046,"stem":3047},"Вступ до безпеки баз даних","\u002Fdatabases\u002Fsecurity\u002Fintroduction","06.databases\u002F08.security\u002F01.introduction",{"title":3049,"path":3050,"stem":3051},"Системні представлення та метадані","\u002Fdatabases\u002Fsecurity\u002Fsystem-views","06.databases\u002F08.security\u002F02.system-views",{"title":3053,"icon":3054,"path":3055,"stem":3056,"children":3057,"page":59},"Резервне копіювання та відновлення","i-lucide-database-backup","\u002Fdatabases\u002Fbackup-recovery","06.databases\u002F09.backup-recovery",[3058],{"title":3053,"path":3059,"stem":3060},"\u002Fdatabases\u002Fbackup-recovery\u002Fbackup-restore","06.databases\u002F09.backup-recovery\u002F01.backup-restore",{"title":3062,"icon":3063,"path":3064,"stem":3065,"children":3066,"page":59},"Повнотекстовий пошук","i-lucide-search","\u002Fdatabases\u002Ffull-text-search","06.databases\u002F10.full-text-search",[3067],{"title":3062,"path":3068,"stem":3069},"\u002Fdatabases\u002Ffull-text-search\u002Ffull-text-search","06.databases\u002F10.full-text-search\u002F01.full-text-search",{"title":3071,"icon":3072,"path":3073,"stem":3074,"children":3075,"page":59},"Tools","i-lucide-wrench","\u002Ftools","07.tools",[3076,3152],{"title":3077,"icon":3078,"path":3079,"stem":3080,"children":3081},"Docker","i-simple-icons-docker","\u002Ftools\u002Fdocker","07.tools\u002F01.docker\u002Findex",[3082,3084,3088,3092,3096,3100,3104,3108,3112,3116,3120,3124,3128,3132,3136,3140,3144,3148],{"title":3083,"path":3079,"stem":3080},"Docker: від нуля до production",{"title":3085,"path":3086,"stem":3087},"Контейнеризація — від проблеми до рішення","\u002Ftools\u002Fdocker\u002Fcontainerization-concept","07.tools\u002F01.docker\u002F01.containerization-concept",{"title":3089,"path":3090,"stem":3091},"Docker — що це і навіщо?","\u002Ftools\u002Fdocker\u002Fdocker-what-and-why","07.tools\u002F01.docker\u002F02.docker-what-and-why",{"title":3093,"path":3094,"stem":3095},"Архітектура Docker Engine","\u002Ftools\u002Fdocker\u002Fdocker-architecture","07.tools\u002F01.docker\u002F03.docker-architecture",{"title":3097,"path":3098,"stem":3099},"Встановлення Docker","\u002Ftools\u002Fdocker\u002Finstallation","07.tools\u002F01.docker\u002F04.installation",{"title":3101,"path":3102,"stem":3103},"Перший контейнер — docker run","\u002Ftools\u002Fdocker\u002Ffirst-container","07.tools\u002F01.docker\u002F05.first-container",{"title":3105,"path":3106,"stem":3107},"Життєвий цикл контейнера","\u002Ftools\u002Fdocker\u002Fcontainer-lifecycle","07.tools\u002F01.docker\u002F06.container-lifecycle",{"title":3109,"path":3110,"stem":3111},"Docker Images — фундаментальні концепції","\u002Ftools\u002Fdocker\u002Fdocker-images-fundamentals","07.tools\u002F01.docker\u002F07.docker-images-fundamentals",{"title":3113,"path":3114,"stem":3115},"Dockerfile — основи","\u002Ftools\u002Fdocker\u002Fdockerfile-basics","07.tools\u002F01.docker\u002F08.dockerfile-basics",{"title":3117,"path":3118,"stem":3119},"Dockerfile — просунуті техніки","\u002Ftools\u002Fdocker\u002Fdockerfile-advanced","07.tools\u002F01.docker\u002F09.dockerfile-advanced",{"title":3121,"path":3122,"stem":3123},"Build Context та кешування шарів","\u002Ftools\u002Fdocker\u002Fbuild-context-and-cache","07.tools\u002F01.docker\u002F10.build-context-and-cache",{"title":3125,"path":3126,"stem":3127},"Реєстри Docker-образів","\u002Ftools\u002Fdocker\u002Fimage-registries","07.tools\u002F01.docker\u002F11.image-registries",{"title":3129,"path":3130,"stem":3131},"Контейнеризація .NET додатків","\u002Ftools\u002Fdocker\u002Fdotnet-containerization","07.tools\u002F01.docker\u002F12.dotnet-containerization",{"title":3133,"path":3134,"stem":3135},"Томи та збереження даних","\u002Ftools\u002Fdocker\u002Fvolumes-and-data","07.tools\u002F01.docker\u002F13.volumes-and-data",{"title":3137,"path":3138,"stem":3139},"Основи мережі в Docker","\u002Ftools\u002Fdocker\u002Fnetworking-basics","07.tools\u002F01.docker\u002F14.networking-basics",{"title":3141,"path":3142,"stem":3143},"Змінні оточення та конфігурація","\u002Ftools\u002Fdocker\u002Fenvironment-and-configuration","07.tools\u002F01.docker\u002F15.environment-and-configuration",{"title":3145,"path":3146,"stem":3147},"Docker Compose — оркестрація контейнерів","\u002Ftools\u002Fdocker\u002Fdocker-compose-basics","07.tools\u002F01.docker\u002F16.docker-compose-basics",{"title":3149,"path":3150,"stem":3151},"Docker Compose — Multi-Service застосунки","\u002Ftools\u002Fdocker\u002Fcompose-multi-service","07.tools\u002F01.docker\u002F17.compose-multi-service",{"title":3153,"icon":3154,"path":3155,"stem":3156,"children":3157},"Kubernetes","simple-icons:kubernetes","\u002Ftools\u002Fkubernetes","07.tools\u002F02.kubernetes\u002Findex",[3158,3160,3164,3168,3172,3176,3180,3184,3188],{"title":3159,"path":3155,"stem":3156},"Kubernetes: від розробки до production",{"title":3161,"path":3162,"stem":3163},"Kubernetes — коли Docker Compose більше не вистачає","\u002Ftools\u002Fkubernetes\u002Fwhy-kubernetes","07.tools\u002F02.kubernetes\u002F01.why-kubernetes",{"title":3165,"path":3166,"stem":3167},"Архітектура Kubernetes — анатомія кластера","\u002Ftools\u002Fkubernetes\u002Fkubernetes-architecture","07.tools\u002F02.kubernetes\u002F02.kubernetes-architecture",{"title":3169,"path":3170,"stem":3171},"Локальне середовище — minikube, kind та k3s","\u002Ftools\u002Fkubernetes\u002Flocal-environment","07.tools\u002F02.kubernetes\u002F03.local-environment",{"title":3173,"path":3174,"stem":3175},"Pod — атомарна одиниця Kubernetes","\u002Ftools\u002Fkubernetes\u002Fpods-and-containers","07.tools\u002F02.kubernetes\u002F04.pods-and-containers",{"title":3177,"path":3178,"stem":3179},"Патерни використання Pod","\u002Ftools\u002Fkubernetes\u002Fpod-patterns","07.tools\u002F02.kubernetes\u002F05.pod-patterns",{"title":3181,"path":3182,"stem":3183},"Deployment — декларативне управління Pod","\u002Ftools\u002Fkubernetes\u002Fdeployment-basics","07.tools\u002F02.kubernetes\u002F06.deployment-basics",{"title":3185,"path":3186,"stem":3187},"Rolling Updates та управління життєвим циклом Deployment","\u002Ftools\u002Fkubernetes\u002Fdeployment-rolling-updates","07.tools\u002F02.kubernetes\u002F07.deployment-rolling-updates",{"title":3189,"path":3190,"stem":3191},"Service — мережева абстракція для Pod","\u002Ftools\u002Fkubernetes\u002Fservices-networking","07.tools\u002F02.kubernetes\u002F08.services-networking",{"title":3193,"icon":3194,"path":3195,"stem":3196,"children":3197,"page":59},"Software Engineering","i-lucide-code-2","\u002Fsoftware-engineering","09.software-engineering",[3198,3202,3206,3210,3214,3218,3222,3226,3230,3234,3238],{"title":3199,"path":3200,"stem":3201},"1. Аналіз предметної області. Експертні знання та складність","\u002Fsoftware-engineering\u002Fintro-subdomains","09.software-engineering\u002F01.intro-subdomains",{"title":3203,"path":3204,"stem":3205},"2. Обмежені контексти. Інтеграція обмежених контекстів","\u002Fsoftware-engineering\u002Fintegrating-limited-contexts","09.software-engineering\u002F02.integrating-limited-contexts",{"title":3207,"path":3208,"stem":3209},"3. Реалізація простої бізнес-логіки","\u002Fsoftware-engineering\u002Fsimple","09.software-engineering\u002F03.simple",{"title":3211,"path":3212,"stem":3213},"4. Опрацювання складної бізнес-логіки","\u002Fsoftware-engineering\u002Fcomplex-business-logic","09.software-engineering\u002F04.complex-business-logic",{"title":3215,"path":3216,"stem":3217},"5. Моделювання фактора часу. Подієво-орієнтована архітектура.","\u002Fsoftware-engineering\u002Fmodelling-the-time-factor","09.software-engineering\u002F05.modelling-the-time-factor",{"title":3219,"path":3220,"stem":3221},"6. Архітектурні патерни","\u002Fsoftware-engineering\u002Farchitectural-patterns","09.software-engineering\u002F06.architectural-patterns",{"title":3223,"path":3224,"stem":3225},"Паттерни взаємодії","\u002Fsoftware-engineering\u002Fpatterns-of-interaction","09.software-engineering\u002F07.patterns-of-interaction",{"title":3227,"path":3228,"stem":3229},"Евристика проєктування","\u002Fsoftware-engineering\u002Fdesign-heuristics","09.software-engineering\u002F08.design-heuristics",{"title":3231,"path":3232,"stem":3233},"Еволюція проєктних рішень","\u002Fsoftware-engineering\u002Fevolution-of-design-solutions","09.software-engineering\u002F09.evolution-of-design-solutions",{"title":3235,"path":3236,"stem":3237},"EventStorming","\u002Fsoftware-engineering\u002Feventstorming","09.software-engineering\u002F10.eventstorming",{"title":3239,"path":3240,"stem":3241},"DDD на практиці","\u002Fsoftware-engineering\u002Fddd-in-practice","09.software-engineering\u002F11.ddd-in-practice",{"title":3243,"icon":943,"path":3244,"stem":3245,"children":3246,"page":59},"DDD","\u002Fddd","10.ddd",[3247,3251,3255,3259,3263,3267,3271,3275,3279,3283,3287,3291,3295],{"title":3248,"path":3249,"stem":3250},"Аналіз предметної області","\u002Fddd\u002Fdomain-analysis","10.ddd\u002F01.domain-analysis",{"title":3252,"path":3253,"stem":3254},"Експертні знання про предметну область","\u002Fddd\u002Fdomain-expert-knowledge","10.ddd\u002F02.domain-expert-knowledge",{"title":3256,"path":3257,"stem":3258},"Як осмислити складність предметної області","\u002Fddd\u002Fmanaging-domain-complexity","10.ddd\u002F03.managing-domain-complexity",{"title":3260,"path":3261,"stem":3262},"Інтеграція обмежених контекстів","\u002Fddd\u002Fbounded-context-integration","10.ddd\u002F04.bounded-context-integration",{"title":3264,"path":3265,"stem":3266},"Реалізація простої бізнес-логіки","\u002Fddd\u002Fsimple-business-logic","10.ddd\u002F05.simple-business-logic",{"title":3268,"path":3269,"stem":3270},"Обробка складної бізнес-логіки","\u002Fddd\u002Fcomplex-business-logic","10.ddd\u002F06.complex-business-logic",{"title":3272,"path":3273,"stem":3274},"Моделювання фактора часу","\u002Fddd\u002Ftime-modeling","10.ddd\u002F07.time-modeling",{"title":3276,"path":3277,"stem":3278},"Глава 8. Архітектурні Патерни","\u002Fddd\u002Farchitectural-patterns","10.ddd\u002F08.architectural-patterns",{"title":3280,"path":3281,"stem":3282},"Глава 9. Патерни Взаємодії","\u002Fddd\u002Finteraction-patterns","10.ddd\u002F09.interaction-patterns",{"title":3284,"path":3285,"stem":3286},"Глава 10. Проектні Евристики","\u002Fddd\u002Fdesign-heuristics","10.ddd\u002F10.design-heuristics",{"title":3288,"path":3289,"stem":3290},"Глава 11. Еволюція Проектних Рішень","\u002Fddd\u002Fevolution-of-design-decisions","10.ddd\u002F11.evolution-of-design-decisions",{"title":3292,"path":3293,"stem":3294},"Глава 12. EventStorming","\u002Fddd\u002Fevent-storming","10.ddd\u002F12.event-storming",{"title":3296,"path":3297,"stem":3298},"Глава 13. DDD на Практиці","\u002Fddd\u002Fddd-in-practice","10.ddd\u002F13.ddd-in-practice",{"title":3300,"icon":3301,"path":3302,"stem":3303,"children":3304,"page":59},"Media Streaming","i-lucide-video","\u002Fmedia-streaming","11.media-streaming",[3305,3309,3313,3317,3321,3325,3329],{"title":3306,"path":3307,"stem":3308},"01. Магія Стрімінгу: Що відбувається, коли ви натискаєте \"Play\"","\u002Fmedia-streaming\u002Fintroduction","11.media-streaming\u002F01.introduction",{"title":3310,"path":3311,"stem":3312},"02. Анатомія Медіа: Кодеки, Контейнери та Стиснення","\u002Fmedia-streaming\u002Faudio-video-anatomy","11.media-streaming\u002F02.audio-video-anatomy",{"title":3314,"path":3315,"stem":3316},"03. The Gym: FFmpeg Deep Dive","\u002Fmedia-streaming\u002Fffmpeg-gym","11.media-streaming\u002F03.ffmpeg-gym",{"title":3318,"path":3319,"stem":3320},"04. HLS Protocol: HTTP Live Streaming у Деталях","\u002Fmedia-streaming\u002Fhls-protocol","11.media-streaming\u002F04.hls-protocol",{"title":3322,"path":3323,"stem":3324},"05. DASH Protocol: Відкритий Стандарт","\u002Fmedia-streaming\u002Fdash-protocol","11.media-streaming\u002F05.dash-protocol",{"title":3326,"path":3327,"stem":3328},"06. Масштабування: CDN та Adaptive Bitrate","\u002Fmedia-streaming\u002Fcdn-and-adaptive-bitrate","11.media-streaming\u002F06.cdn-and-adaptive-bitrate",{"title":3330,"path":3331,"stem":3332},"07. Війна із Затримкою (Latency)","\u002Fmedia-streaming\u002Frealtime-latency","11.media-streaming\u002F07.realtime-latency",{"title":3334,"icon":3335,"path":3336,"stem":3337,"children":3338,"page":59},"HTML & CSS","i-devicon-html5","\u002Fhtml-css","12.html-css",[3339,3343,3347,3351,3355,3359,3363,3367,3371,3375,3379,3383,3387,3391,3395,3399,3403,3407,3411,3415,3419,3423,3427,3431,3435,3439,3443,3447,3451,3455],{"title":3340,"path":3341,"stem":3342},"Вступ до HTML. Структура документа","\u002Fhtml-css\u002Fintro-html-structure","12.html-css\u002F01.intro-html-structure",{"title":3344,"path":3345,"stem":3346},"Форматування тексту в HTML","\u002Fhtml-css\u002Fhtml-text-formatting","12.html-css\u002F02.html-text-formatting",{"title":3348,"path":3349,"stem":3350},"Посилання та зображення в HTML","\u002Fhtml-css\u002Fhtml-links-images","12.html-css\u002F03.html-links-images",{"title":3352,"path":3353,"stem":3354},"Списки та таблиці в HTML","\u002Fhtml-css\u002Fhtml-lists-tables","12.html-css\u002F04.html-lists-tables",{"title":3356,"path":3357,"stem":3358},"Форми в HTML","\u002Fhtml-css\u002Fhtml-forms","12.html-css\u002F05.html-forms",{"title":3360,"path":3361,"stem":3362},"Семантичні елементи HTML5","\u002Fhtml-css\u002Fhtml-semantic-elements","12.html-css\u002F06.html-semantic-elements",{"title":3364,"path":3365,"stem":3366},"Мультимедіа та розширені елементи HTML","\u002Fhtml-css\u002Fhtml-multimedia-advanced","12.html-css\u002F07.html-multimedia-advanced",{"title":3368,"path":3369,"stem":3370},"Мікророзмітка та SEO в HTML","\u002Fhtml-css\u002Fhtml-microdata-seo","12.html-css\u002F08.html-microdata-seo",{"title":3372,"path":3373,"stem":3374},"Вступ до CSS. Селектори та специфічність","\u002Fhtml-css\u002Fcss-intro-selectors","12.html-css\u002F09.css-intro-selectors",{"title":3376,"path":3377,"stem":3378},"Блокова модель CSS. Відступи. Box Sizing","\u002Fhtml-css\u002Fcss-box-model","12.html-css\u002F10.css-box-model",{"title":3380,"path":3381,"stem":3382},"Розміри у CSS: повний довідник одиниць і ключових слів","\u002Fhtml-css\u002F10a.css-sizing","12.html-css\u002F10a.css-sizing",{"title":3384,"path":3385,"stem":3386},"Типографіка в CSS. Шрифти та текст","\u002Fhtml-css\u002Fcss-typography","12.html-css\u002F11.css-typography",{"title":3388,"path":3389,"stem":3390},"Кольори та фони в CSS","\u002Fhtml-css\u002Fcss-colors-backgrounds","12.html-css\u002F12.css-colors-backgrounds",{"title":3392,"path":3393,"stem":3394},"Тіні та фільтри в CSS","\u002Fhtml-css\u002F12b.css-shadows-filters","12.html-css\u002F12b.css-shadows-filters",{"title":3396,"path":3397,"stem":3398},"CSS Flexbox: Фундамент гнучких макетів","\u002Fhtml-css\u002Fcss-flexbox-fundamentals","12.html-css\u002F13.css-flexbox-fundamentals",{"title":3400,"path":3401,"stem":3402},"CSS Flexbox: Вирівнювання та Позиціонування","\u002Fhtml-css\u002Fcss-flexbox-alignment-sizing-and-patterns","12.html-css\u002F14.css-flexbox-alignment-sizing-and-patterns",{"title":3404,"path":3405,"stem":3406},"CSS Grid. Двовимірний макет. Частина 1","\u002Fhtml-css\u002Fcss-layout-grid","12.html-css\u002F15.css-layout-grid",{"title":3408,"path":3409,"stem":3410},"CSS Grid. Двовимірний макет. Частина 2","\u002Fhtml-css\u002Fcss-layout-grid-advanced","12.html-css\u002F16.css-layout-grid-advanced",{"title":3412,"path":3413,"stem":3414},"Позиціонування в CSS. Z-index. Stacking Context","\u002Fhtml-css\u002Fcss-positioning","12.html-css\u002F17.css-positioning",{"title":3416,"path":3417,"stem":3418},"CSS Анімації та Переходи","\u002Fhtml-css\u002Fcss-animations-transitions","12.html-css\u002F18.css-animations-transitions",{"title":3420,"path":3421,"stem":3422},"Адаптивний дизайн. Media Queries. Частина 1","\u002Fhtml-css\u002Fcss-responsive-media-queries","12.html-css\u002F19.css-responsive-media-queries",{"title":3424,"path":3425,"stem":3426},"Адаптивний дизайн. Частина 2: clamp(), Container Queries, @layer","\u002Fhtml-css\u002Fcss-responsive-advanced","12.html-css\u002F20.css-responsive-advanced",{"title":3428,"path":3429,"stem":3430},"CSS Custom Properties. Методології. Сучасний CSS","\u002Fhtml-css\u002Fcss-variables-methodologies","12.html-css\u002F21.css-variables-methodologies",{"title":3432,"path":3433,"stem":3434},"Сучасний CSS 2023–2025: Нові можливості","\u002Fhtml-css\u002Fcss-modern-features","12.html-css\u002F22.css-modern-features",{"title":3436,"path":3437,"stem":3438},"CSS Nesting, @layer, @scope та @property: нативний препроцесор","\u002Fhtml-css\u002F22a.css-nesting-modern-syntax","12.html-css\u002F22a.css-nesting-modern-syntax",{"title":3440,"path":3441,"stem":3442},"CSS для форм та інтерактивних станів","\u002Fhtml-css\u002Fcss-forms-interactive-states","12.html-css\u002F23.css-forms-interactive-states",{"title":3444,"path":3445,"stem":3446},"Доступність у CSS (CSS Accessibility)","\u002Fhtml-css\u002Fcss-accessibility","12.html-css\u002F24.css-accessibility",{"title":3448,"path":3449,"stem":3450},"CSS-функції та сучасні sizing primitives","\u002Fhtml-css\u002Fcss-functions-sizing","12.html-css\u002F25.css-functions-sizing",{"title":3452,"path":3453,"stem":3454},"Rendering Pipeline і CSS Performance","\u002Fhtml-css\u002Fcss-rendering-performance","12.html-css\u002F26.css-rendering-performance",{"title":3456,"path":3457,"stem":3458},"CSS Best Practices: типові ситуації та правильні рішення","\u002Fhtml-css\u002Fcss-best-practices","12.html-css\u002F27.css-best-practices",{"title":3460,"path":3461,"stem":3462,"children":3463,"page":59},"AWS","\u002Faws","13.aws",[3464,3468,3472,3476,3480,3484,3488,3492,3496,3500,3504,3508,3512,3516,3520,3524,3528,3532],{"title":3465,"path":3466,"stem":3467},"Реєстрація AWS акаунту та студентські програми","\u002Faws\u002Faccount-registration","13.aws\u002F00.account-registration",{"title":3469,"path":3470,"stem":3471},"Вступ до хмарних обчислень та AWS","\u002Faws\u002Fintroduction-to-cloud","13.aws\u002F01.introduction-to-cloud",{"title":3473,"path":3474,"stem":3475},"AWS IAM — Identity and Access Management","\u002Faws\u002Fiam","13.aws\u002F02.iam",{"title":3477,"path":3478,"stem":3479},"AWS IAM CLI — Довідник команд","\u002Faws\u002F02a.iam-doc","13.aws\u002F02a.iam-doc",{"title":3481,"path":3482,"stem":3483},"Docker та контейнеризація в AWS — ECR, ECS та Fargate","\u002Faws\u002Fdocker-ecs","13.aws\u002F03.docker-ecs",{"title":3485,"path":3486,"stem":3487},"AWS ECR \u002F ECS CLI — Довідник команд","\u002Faws\u002F03a.docker-ecs-doc","13.aws\u002F03a.docker-ecs-doc",{"title":3489,"path":3490,"stem":3491},"Amazon EC2 — Elastic Compute Cloud","\u002Faws\u002Fec2","13.aws\u002F04.ec2",{"title":3493,"path":3494,"stem":3495},"AWS EC2 CLI — Довідник команд","\u002Faws\u002F04a.ec2-doc","13.aws\u002F04a.ec2-doc",{"title":3497,"path":3498,"stem":3499},"Elastic Load Balancing та Auto Scaling","\u002Faws\u002Falb-asg","13.aws\u002F05.alb-asg",{"title":3501,"path":3502,"stem":3503},"Amazon S3 — Simple Storage Service","\u002Faws\u002Fs3","13.aws\u002F06.s3",{"title":3505,"path":3506,"stem":3507},"Amazon CloudFront — Content Delivery Network","\u002Faws\u002Fcloudfront","13.aws\u002F07.cloudfront",{"title":3509,"path":3510,"stem":3511},"Amazon RDS — Relational Database Service","\u002Faws\u002Frds","13.aws\u002F08.rds",{"title":3513,"path":3514,"stem":3515},"Amazon DynamoDB — NoSQL Database","\u002Faws\u002Fdynamodb","13.aws\u002F09.dynamodb",{"title":3517,"path":3518,"stem":3519},"AWS Lambda та Serverless Compute","\u002Faws\u002Flambda","13.aws\u002F10.lambda",{"title":3521,"path":3522,"stem":3523},"Amazon Bedrock - Foundation Models, RAG та Agents","\u002Faws\u002Fbedrock","13.aws\u002F22.bedrock",{"title":3525,"path":3526,"stem":3527},"Amazon Rekognition - Комп'ютерний зір","\u002Faws\u002Frekognition","13.aws\u002F23.rekognition",{"title":3529,"path":3530,"stem":3531},"Amazon Textract - Інтелектуальний аналіз документів","\u002Faws\u002Ftextract","13.aws\u002F24.textract",{"title":3533,"path":3534,"stem":3535},"Amazon Polly, Transcribe, Comprehend та Translate","\u002Faws\u002Faudio-nlp-services","13.aws\u002F25.audio-nlp-services",{"title":3537,"path":3538,"stem":3539,"children":3540,"page":59},"Ai Development","\u002Fai-development","14.ai-development",[3541],{"title":3542,"path":3543,"stem":3544,"children":3545,"page":59},"Foundations","\u002Fai-development\u002Ffoundations","14.ai-development\u002F00.foundations",[3546],{"title":3547,"path":3548,"stem":3549},"Модуль 0.1. Що таке Large Language Model (LLM)","\u002Fai-development\u002Ffoundations\u002Fwhat-is-llm","14.ai-development\u002F00.foundations\u002F01.what-is-llm",{"title":3551,"path":3552,"stem":3553,"children":3554,"page":59},"React Native","\u002Freact-native","15.react-native",[3555,3576],{"title":3556,"path":3557,"stem":3558,"children":3559,"page":59},"Version1","\u002Freact-native\u002Fversion1","15.react-native\u002Fversion1",[3560,3564,3568,3572],{"title":3561,"path":3562,"stem":3563},"Що таке React Native","\u002Freact-native\u002Fversion1\u002Fwhat-is-react-native","15.react-native\u002Fversion1\u002F01.what-is-react-native",{"title":3565,"path":3566,"stem":3567},"Як влаштований React Native «під капотом»","\u002Freact-native\u002Fversion1\u002Freact-native-architecture","15.react-native\u002Fversion1\u002F02.react-native-architecture",{"title":3569,"path":3570,"stem":3571},"Від React до React Native: ключові відмінності","\u002Freact-native\u002Fversion1\u002Freact-native-vs-react","15.react-native\u002Fversion1\u002F03.react-native-vs-react",{"title":3573,"path":3574,"stem":3575},"View та Text — фундаментальні будівельні блоки","\u002Freact-native\u002Fversion1\u002Fview-and-text","15.react-native\u002Fversion1\u002F04.view-and-text",{"title":3577,"path":3578,"stem":3579,"children":3580,"page":59},"Version2","\u002Freact-native\u002Fversion2","15.react-native\u002Fversion2",[3581,3585,3589,3593,3597,3601,3605,3609,3613,3617,3621,3625,3629,3633,3637,3641,3645,3649,3653,3657,3661,3665,3669,3673,3677,3681,3685],{"title":3582,"path":3583,"stem":3584},"React Native — навіщо він потрібен","\u002Freact-native\u002Fversion2\u002Fwhy-react-native","15.react-native\u002Fversion2\u002F01.why-react-native",{"title":3586,"path":3587,"stem":3588},"Налаштування середовища розробки","\u002Freact-native\u002Fversion2\u002Fenvironment-setup","15.react-native\u002Fversion2\u002F02.environment-setup",{"title":3590,"path":3591,"stem":3592},"Expo і класичний React Native — два шляхи одного фреймворку","\u002Freact-native\u002Fversion2\u002Fexpo-vs-cli-landscape","15.react-native\u002Fversion2\u002F03.expo-vs-cli-landscape",{"title":3594,"path":3595,"stem":3596},"Середовище Expo — перший запуск","\u002Freact-native\u002Fversion2\u002Fexpo-setup-and-tooling","15.react-native\u002Fversion2\u002F04.expo-setup-and-tooling",{"title":3598,"path":3599,"stem":3600},"Структура проєкту та перші UI-примітиви","\u002Freact-native\u002Fversion2\u002Fproject-structure-and-conventions","15.react-native\u002Fversion2\u002F05.project-structure-and-conventions",{"title":3602,"path":3603,"stem":3604},"Базові компоненти React Native","\u002Freact-native\u002Fversion2\u002Fcore-components","15.react-native\u002Fversion2\u002F06.core-components",{"title":3606,"path":3607,"stem":3608},"Flexbox і layout у React Native","\u002Freact-native\u002Fversion2\u002Fflexbox-and-layout","15.react-native\u002Fversion2\u002F07.flexbox-and-layout",{"title":3610,"path":3611,"stem":3612},"StyleSheet і теми (light \u002F dark)","\u002Freact-native\u002Fversion2\u002Fstylesheet-and-theming","15.react-native\u002Fversion2\u002F08.stylesheet-and-theming",{"title":3614,"path":3615,"stem":3616},"Списки та віртуалізація","\u002Freact-native\u002Fversion2\u002Flists-and-virtualization","15.react-native\u002Fversion2\u002F09.lists-and-virtualization",{"title":3618,"path":3619,"stem":3620},"Форми, ввід і валідація","\u002Freact-native\u002Fversion2\u002Fforms-input-validation","15.react-native\u002Fversion2\u002F10.forms-input-validation",{"title":3622,"path":3623,"stem":3624},"Основи Expo Router","\u002Freact-native\u002Fversion2\u002Fexpo-router-basics","15.react-native\u002Fversion2\u002F11.expo-router-basics",{"title":3626,"path":3627,"stem":3628},"Вкладені навігатори та модалки","\u002Freact-native\u002Fversion2\u002Fnested-navigators-and-modals","15.react-native\u002Fversion2\u002F12.nested-navigators-and-modals",{"title":3630,"path":3631,"stem":3632},"Deep linking і параметри маршрутів","\u002Freact-native\u002Fversion2\u002Fdeep-linking-and-params","15.react-native\u002Fversion2\u002F13.deep-linking-and-params",{"title":3634,"path":3635,"stem":3636},"Робота з мережею та життєвий цикл застосунку","\u002Freact-native\u002Fversion2\u002Fnetworking-and-app-lifecycle","15.react-native\u002Fversion2\u002F14.networking-and-app-lifecycle",{"title":3638,"path":3639,"stem":3640},"Redux Toolkit на мобільних платформах","\u002Freact-native\u002Fversion2\u002Fredux-toolkit-on-mobile","15.react-native\u002Fversion2\u002F15.redux-toolkit-on-mobile",{"title":3642,"path":3643,"stem":3644},"RTK Query на мобільних платформах","\u002Freact-native\u002Fversion2\u002Frtk-query-mobile","15.react-native\u002Fversion2\u002F16.rtk-query-mobile",{"title":3646,"path":3647,"stem":3648},"Автентифікація та Безпечне Зберігання","\u002Freact-native\u002Fversion2\u002Fauth-secure-storage","15.react-native\u002Fversion2\u002F18.auth-secure-storage",{"title":3650,"path":3651,"stem":3652},"Локальне сховище — AsyncStorage, MMKV, SQLite","\u002Freact-native\u002Fversion2\u002Flocal-storage-mmkv-sqlite","15.react-native\u002Fversion2\u002F19.local-storage-mmkv-sqlite",{"title":3654,"path":3655,"stem":3656},"Офлайн-First і синхронізація","\u002Freact-native\u002Fversion2\u002Foffline-first-sync","15.react-native\u002Fversion2\u002F20.offline-first-sync",{"title":3658,"path":3659,"stem":3660},"Камера, медіа та файлова система","\u002Freact-native\u002Fversion2\u002Fcamera-media-filesystem","15.react-native\u002Fversion2\u002F21.camera-media-filesystem",{"title":3662,"path":3663,"stem":3664},"Карти та геолокація","\u002Freact-native\u002Fversion2\u002Fmaps-and-location","15.react-native\u002Fversion2\u002F22.maps-and-location",{"title":3666,"path":3667,"stem":3668},"Жести та дотики","\u002Freact-native\u002Fversion2\u002Fgestures-and-touch","15.react-native\u002Fversion2\u002F23.gestures-and-touch",{"title":3670,"path":3671,"stem":3672},"Анімації з Reanimated","\u002Freact-native\u002Fversion2\u002Freanimated-animations","15.react-native\u002Fversion2\u002F24.reanimated-animations",{"title":3674,"path":3675,"stem":3676},"Push і локальні сповіщення","\u002Freact-native\u002Fversion2\u002Fpush-and-local-notifications","15.react-native\u002Fversion2\u002F25.push-and-local-notifications",{"title":3678,"path":3679,"stem":3680},"Інтернаціоналізація та доступність","\u002Freact-native\u002Fversion2\u002Faccessibility-and-i18n","15.react-native\u002Fversion2\u002F26.accessibility-and-i18n",{"title":3682,"path":3683,"stem":3684},"EAS Build та профілі збірки","\u002Freact-native\u002Fversion2\u002Feas-build-and-profiles","15.react-native\u002Fversion2\u002F29.eas-build-and-profiles",{"title":3686,"path":3687,"stem":3688},"Публікація в App Store та Google Play","\u002Freact-native\u002Fversion2\u002Fapp-store-play-release","15.react-native\u002Fversion2\u002F30.app-store-play-release",{"title":3690,"path":3691,"stem":3692,"children":3693,"page":59},"Ai Python","\u002Fai-python","16.ai-python",[3694,3703,3712,3721,3750,3776,3801,3834,3851,3864,3873,3886,3895,3916,3929,3950],{"title":3695,"path":3696,"stem":3697,"children":3698,"page":59},"Intro To Ai","\u002Fai-python\u002Fintro-to-ai","16.ai-python\u002F01.intro-to-ai",[3699],{"title":3700,"path":3701,"stem":3702},"Вступ до штучного інтелекту та машинного навчання","\u002Fai-python\u002Fintro-to-ai\u002Fintro-to-ai","16.ai-python\u002F01.intro-to-ai\u002F01.intro-to-ai",{"title":3704,"path":3705,"stem":3706,"children":3707,"page":59},"Jupyter Notebook","\u002Fai-python\u002Fjupyter-notebook","16.ai-python\u002F02.jupyter-notebook",[3708],{"title":3709,"path":3710,"stem":3711},"Знайомство з Jupyter Notebook та JupyterLab","\u002Fai-python\u002Fjupyter-notebook\u002Fjupyter-notebook","16.ai-python\u002F02.jupyter-notebook\u002F02.jupyter-notebook",{"title":3713,"path":3714,"stem":3715,"children":3716,"page":59},"Numpy Pandas","\u002Fai-python\u002Fnumpy-pandas","16.ai-python\u002F03.numpy-pandas",[3717],{"title":3718,"path":3719,"stem":3720},"Робота з даними з використанням NumPy і pandas","\u002Fai-python\u002Fnumpy-pandas\u002Fnumpy-pandas","16.ai-python\u002F03.numpy-pandas\u002F03.numpy-pandas",{"title":3722,"path":3723,"stem":3724,"children":3725,"page":59},"Statistics Basics","\u002Fai-python\u002Fstatistics-basics","16.ai-python\u002F04.statistics-basics",[3726,3730,3734,3738,3742,3746],{"title":3727,"path":3728,"stem":3729},"Випадкові величини та їх типи","\u002Fai-python\u002Fstatistics-basics\u002Frandom-variables","16.ai-python\u002F04.statistics-basics\u002F01.random-variables",{"title":3731,"path":3732,"stem":3733},"Міри центральної тенденції","\u002Fai-python\u002Fstatistics-basics\u002Fcentral-tendency","16.ai-python\u002F04.statistics-basics\u002F02.central-tendency",{"title":3735,"path":3736,"stem":3737},"Міри варіації (розсіювання)","\u002Fai-python\u002Fstatistics-basics\u002Fvariability","16.ai-python\u002F04.statistics-basics\u002F03.variability",{"title":3739,"path":3740,"stem":3741},"Розподіли випадкових величин","\u002Fai-python\u002Fstatistics-basics\u002Fdistributions","16.ai-python\u002F04.statistics-basics\u002F04.distributions",{"title":3743,"path":3744,"stem":3745},"Кореляція та взаємозв'язки","\u002Fai-python\u002Fstatistics-basics\u002Fcorrelation","16.ai-python\u002F04.statistics-basics\u002F05.correlation",{"title":3747,"path":3748,"stem":3749},"Викиди та аномалії","\u002Fai-python\u002Fstatistics-basics\u002Foutliers","16.ai-python\u002F04.statistics-basics\u002F06.outliers",{"title":3751,"path":3752,"stem":3753,"children":3754},"Візуалізація даних за допомогою matplotlib","\u002Fai-python\u002Fdata-visualization","16.ai-python\u002F05.data-visualization\u002Findex",[3755,3756,3760,3764,3768,3772],{"title":3751,"path":3752,"stem":3753},{"title":3757,"path":3758,"stem":3759},"Основи matplotlib — Figure, Axes та перший графік","\u002Fai-python\u002Fdata-visualization\u002Fmatplotlib-basics","16.ai-python\u002F05.data-visualization\u002F01.matplotlib-basics",{"title":3761,"path":3762,"stem":3763},"Візуалізація розподілів — гістограми та box plots","\u002Fai-python\u002Fdata-visualization\u002Fdistributions","16.ai-python\u002F05.data-visualization\u002F02.distributions",{"title":3765,"path":3766,"stem":3767},"Візуалізація залежностей — scatter plots та кореляція","\u002Fai-python\u002Fdata-visualization\u002Frelationships","16.ai-python\u002F05.data-visualization\u002F03.relationships",{"title":3769,"path":3770,"stem":3771},"Візуалізація категоріальних даних — bar charts та pie charts","\u002Fai-python\u002Fdata-visualization\u002Fcategorical","16.ai-python\u002F05.data-visualization\u002F04.categorical",{"title":3773,"path":3774,"stem":3775},"Композиції та стилізація — subplots та багатопанельні дашборди","\u002Fai-python\u002Fdata-visualization\u002Fsubplots-styling","16.ai-python\u002F05.data-visualization\u002F05.subplots-styling",{"title":3777,"path":3778,"stem":3779,"children":3780,"page":59},"Linear Regression","\u002Fai-python\u002Flinear-regression","16.ai-python\u002F06.linear-regression",[3781,3785,3789,3793,3797],{"title":3782,"path":3783,"stem":3784},"Регресійна задача та лінійна регресія з однією змінною","\u002Fai-python\u002Flinear-regression\u002Fregression-task","16.ai-python\u002F06.linear-regression\u002F01.regression-task",{"title":3786,"path":3787,"stem":3788},"Метрики якості регресії — як оцінити модель","\u002Fai-python\u002Flinear-regression\u002Fquality-metrics","16.ai-python\u002F06.linear-regression\u002F02.quality-metrics",{"title":3790,"path":3791,"stem":3792},"Градієнтний спуск — як модель навчається","\u002Fai-python\u002Flinear-regression\u002Fgradient-descent","16.ai-python\u002F06.linear-regression\u002F03.gradient-descent",{"title":3794,"path":3795,"stem":3796},"Множинна лінійна регресія — модель з багатьма ознаками","\u002Fai-python\u002Flinear-regression\u002Fmultiple-regression","16.ai-python\u002F06.linear-regression\u002F04.multiple-regression",{"title":3798,"path":3799,"stem":3800},"Практичні завдання та резюме модуля","\u002Fai-python\u002Flinear-regression\u002Fpractice","16.ai-python\u002F06.linear-regression\u002F05.practice",{"title":3802,"path":3803,"stem":3804,"children":3805,"page":59},"Practical Regression","\u002Fai-python\u002Fpractical-regression","16.ai-python\u002F07.practical-regression",[3806,3810,3814,3818,3822,3826,3830],{"title":3807,"path":3808,"stem":3809},"Знайомство з scikit-learn","\u002Fai-python\u002Fpractical-regression\u002Fintro-scikit-learn","16.ai-python\u002F07.practical-regression\u002F01.intro-scikit-learn",{"title":3811,"path":3812,"stem":3813},"Exploratory Data Analysis датасету Housing Prices","\u002Fai-python\u002Fpractical-regression\u002Fhousing-dataset-eda","16.ai-python\u002F07.practical-regression\u002F02.housing-dataset-eda",{"title":3815,"path":3816,"stem":3817},"Попередня обробка даних (Data Preprocessing)","\u002Fai-python\u002Fpractical-regression\u002Fdata-preprocessing","16.ai-python\u002F07.practical-regression\u002F03.data-preprocessing",{"title":3819,"path":3820,"stem":3821},"Train\u002FTest Split та перша повноцінна модель","\u002Fai-python\u002Fpractical-regression\u002Ftrain-test-split","16.ai-python\u002F07.practical-regression\u002F04.train-test-split",{"title":3823,"path":3824,"stem":3825},"Перенавчання та недонавчання","\u002Fai-python\u002Fpractical-regression\u002Foverfitting-underfitting","16.ai-python\u002F07.practical-regression\u002F05.overfitting-underfitting",{"title":3827,"path":3828,"stem":3829},"Крос-валідація (Cross-Validation)","\u002Fai-python\u002Fpractical-regression\u002Fcross-validation","16.ai-python\u002F07.practical-regression\u002F06.cross-validation",{"title":3831,"path":3832,"stem":3833},"Гіперпараметри та їх оптимізація","\u002Fai-python\u002Fpractical-regression\u002Fhyperparameter-tuning","16.ai-python\u002F07.practical-regression\u002F07.hyperparameter-tuning",{"title":3835,"path":3836,"stem":3837,"children":3838,"page":59},"Polynomial Regression","\u002Fai-python\u002Fpolynomial-regression","16.ai-python\u002F08.polynomial-regression",[3839,3843,3847],{"title":3840,"path":3841,"stem":3842},"Нелінійні залежності та поліноміальна регресія","\u002Fai-python\u002Fpolynomial-regression\u002Fnonlinear-patterns","16.ai-python\u002F08.polynomial-regression\u002F01.nonlinear-patterns",{"title":3844,"path":3845,"stem":3846},"Регуляризація в регресії — Ridge та Lasso","\u002Fai-python\u002Fpolynomial-regression\u002Fregularization-methods","16.ai-python\u002F08.polynomial-regression\u002F02.regularization-methods",{"title":3848,"path":3849,"stem":3850},"Практика — поліноміальна регресія та регуляризація на Housing Prices","\u002Fai-python\u002Fpolynomial-regression\u002Fhousing-prices-practice","16.ai-python\u002F08.polynomial-regression\u002F03.housing-prices-practice",{"title":3852,"path":3853,"stem":3854,"children":3855,"page":59},"Decision Trees Random Forest","\u002Fai-python\u002Fdecision-trees-random-forest","16.ai-python\u002F09.decision-trees-random-forest",[3856,3860],{"title":3857,"path":3858,"stem":3859},"Дерева рішень — моделі з прозорою та зрозумілою логікою","\u002Fai-python\u002Fdecision-trees-random-forest\u002Fdecision-trees","16.ai-python\u002F09.decision-trees-random-forest\u002F01.decision-trees",{"title":3861,"path":3862,"stem":3863},"Random Forest — ансамблі дерев рішень","\u002Fai-python\u002Fdecision-trees-random-forest\u002Frandom-forest","16.ai-python\u002F09.decision-trees-random-forest\u002F02.random-forest",{"title":3865,"path":3866,"stem":3867,"children":3868,"page":59},"Gradient Boosting","\u002Fai-python\u002Fgradient-boosting","16.ai-python\u002F10.gradient-boosting",[3869],{"title":3870,"path":3871,"stem":3872},"Градієнтний бустинг та XGBoost — послідовне виправлення помилок","\u002Fai-python\u002Fgradient-boosting\u002Fgradient-boosting-xgboost","16.ai-python\u002F10.gradient-boosting\u002F01.gradient-boosting-xgboost",{"title":3874,"path":3875,"stem":3876,"children":3877,"page":59},"Logistic Regression Classification","\u002Fai-python\u002Flogistic-regression-classification","16.ai-python\u002F11.logistic-regression-classification",[3878,3882],{"title":3879,"path":3880,"stem":3881},"Основи класифікації та логістична регресія","\u002Fai-python\u002Flogistic-regression-classification\u002Fclassification-basics","16.ai-python\u002F11.logistic-regression-classification\u002F01.classification-basics",{"title":3883,"path":3884,"stem":3885},"Метрики якості класифікації","\u002Fai-python\u002Flogistic-regression-classification\u002Fclassification-metrics","16.ai-python\u002F11.logistic-regression-classification\u002F02.classification-metrics",{"title":3887,"path":3888,"stem":3889,"children":3890,"page":59},"Neural Networks Basics","\u002Fai-python\u002Fneural-networks-basics","16.ai-python\u002F12.neural-networks-basics",[3891],{"title":3892,"path":3893,"stem":3894},"Вступ до нейронних мереж — від персептрона до багатошарових мереж","\u002Fai-python\u002Fneural-networks-basics\u002Fperceptron-to-mlp","16.ai-python\u002F12.neural-networks-basics\u002F01.perceptron-to-mlp",{"title":3896,"path":3897,"stem":3898,"children":3899,"page":59},"Pytorch Intro","\u002Fai-python\u002Fpytorch-intro","16.ai-python\u002F13.pytorch-intro",[3900,3904,3908,3912],{"title":3901,"path":3902,"stem":3903},"Тензори та автоматичне диференціювання в PyTorch","\u002Fai-python\u002Fpytorch-intro\u002Ftensors-and-autograd","16.ai-python\u002F13.pytorch-intro\u002F01.tensors-and-autograd",{"title":3905,"path":3906,"stem":3907},"Побудова та тренування нейронних мереж у PyTorch","\u002Fai-python\u002Fpytorch-intro\u002Fbuilding-and-training","16.ai-python\u002F13.pytorch-intro\u002F02.building-and-training",{"title":3909,"path":3910,"stem":3911},"Робота з даними та регуляризація в PyTorch","\u002Fai-python\u002Fpytorch-intro\u002Fdata-and-regularization","16.ai-python\u002F13.pytorch-intro\u002F03.data-and-regularization",{"title":3913,"path":3914,"stem":3915},"Просунуті теми PyTorch — GPU, оптимізація та deployment","\u002Fai-python\u002Fpytorch-intro\u002Fadvanced-topics","16.ai-python\u002F13.pytorch-intro\u002F04.advanced-topics",{"title":3917,"path":3918,"stem":3919,"children":3920,"page":59},"Activation Functions Heart Disease","\u002Fai-python\u002Factivation-functions-heart-disease","16.ai-python\u002F14.activation-functions-heart-disease",[3921,3925],{"title":3922,"path":3923,"stem":3924},"Функції активації та побудова нейронної мережі для передбачення вад серця","\u002Fai-python\u002Factivation-functions-heart-disease\u002Fheart-disease-prediction","16.ai-python\u002F14.activation-functions-heart-disease\u002F01.heart-disease-prediction",{"title":3926,"path":3927,"stem":3928},"Бонус — Повний приклад Student Performance Prediction","\u002Fai-python\u002Factivation-functions-heart-disease\u002Fstudent-performance-bonus","16.ai-python\u002F14.activation-functions-heart-disease\u002F02.student-performance-bonus",{"title":3930,"path":3931,"stem":3932,"children":3933,"page":59},"Cnn Image Classification","\u002Fai-python\u002Fcnn-image-classification","16.ai-python\u002F15.cnn-image-classification",[3934,3938,3942,3946],{"title":3935,"path":3936,"stem":3937},"Основи згорткових нейронних мереж — чому не MLP?","\u002Fai-python\u002Fcnn-image-classification\u002Fcnn-fundamentals","16.ai-python\u002F15.cnn-image-classification\u002F01.cnn-fundamentals",{"title":3939,"path":3940,"stem":3941},"Практична імплементація CNN для класифікації MNIST","\u002Fai-python\u002Fcnn-image-classification\u002Fmnist-classification","16.ai-python\u002F15.cnn-image-classification\u002F02.mnist-classification",{"title":3943,"path":3944,"stem":3945},"Глибші CNN та аугментація даних для CIFAR-10","\u002Fai-python\u002Fcnn-image-classification\u002Fcifar10-and-data-augmentation","16.ai-python\u002F15.cnn-image-classification\u002F03.cifar10-and-data-augmentation",{"title":3947,"path":3948,"stem":3949},"Transfer Learning та візуалізація внутрішніх представлень CNN","\u002Fai-python\u002Fcnn-image-classification\u002Ftransfer-learning-and-visualization","16.ai-python\u002F15.cnn-image-classification\u002F04.transfer-learning-and-visualization",{"title":3951,"path":3952,"stem":3953,"children":3954,"page":59},"Batching Normalization Deep Networks","\u002Fai-python\u002Fbatching-normalization-deep-networks","16.ai-python\u002F16.batching-normalization-deep-networks",[3955,3959,3963,3967,3971,3975,3979],{"title":3956,"path":3957,"stem":3958},"Mini-batch Gradient Descent та ефективна обробка даних","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Fmini-batch-gradient-descent","16.ai-python\u002F16.batching-normalization-deep-networks\u002F01.mini-batch-gradient-descent",{"title":3960,"path":3961,"stem":3962},"Ініціалізація ваг — фундамент стабільного навчання","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Fweight-initialization","16.ai-python\u002F16.batching-normalization-deep-networks\u002F02.weight-initialization",{"title":3964,"path":3965,"stem":3966},"Batch Normalization — прорив у глибокому навчанні","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Fbatch-normalization","16.ai-python\u002F16.batching-normalization-deep-networks\u002F03.batch-normalization",{"title":3968,"path":3969,"stem":3970},"Dropout та комбіновані стратегії регуляризації","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Fdropout-and-regularization","16.ai-python\u002F16.batching-normalization-deep-networks\u002F04.dropout-and-regularization",{"title":3972,"path":3973,"stem":3974},"Проєктування глибоких мереж — від теорії до практики","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Fdeep-networks-architectures","16.ai-python\u002F16.batching-normalization-deep-networks\u002F05.deep-networks-architectures",{"title":3976,"path":3977,"stem":3978},"Планування learning rate — коли та як зменшувати крок навчання","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Flearning-rate-scheduling","16.ai-python\u002F16.batching-normalization-deep-networks\u002F06.learning-rate-scheduling",{"title":3980,"path":3981,"stem":3982},"Підсумковий проєкт — побудова сучасного класифікатора зображень","\u002Fai-python\u002Fbatching-normalization-deep-networks\u002Ffinal-project-deep-classifier","16.ai-python\u002F16.batching-normalization-deep-networks\u002F07.final-project-deep-classifier",{"title":3984,"icon":658,"path":3985,"stem":3986,"children":3987,"page":59},"Клієнт-серверна архітектура","\u002Fclient-server-architecture","17.client-server-architecture",[3988,3992,3996,4000,4004,4008,4012,4016,4020,4024,4028,4032,4036,4040,4044,4048,4052,4056],{"title":3989,"path":3990,"stem":3991},"Архітектура клієнт-серверних систем та основи Go","\u002Fclient-server-architecture\u002Fclient-server-architecture-and-go","17.client-server-architecture\u002F01.client-server-architecture-and-go",{"title":3993,"path":3994,"stem":3995},"Система типів та модель пам’яті мови Go","\u002Fclient-server-architecture\u002Ftype-system-and-memory-model","17.client-server-architecture\u002F02.type-system-and-memory-model",{"title":3997,"path":3998,"stem":3999},"Інтерфейси та модель конкурентності мови Go","\u002Fclient-server-architecture\u002Finterfaces-and-concurrency-model","17.client-server-architecture\u002F03.interfaces-and-concurrency-model",{"title":4001,"path":4002,"stem":4003},"Сокетне програмування та протокол TCP у Go","\u002Fclient-server-architecture\u002Ftcp-socket-programming","17.client-server-architecture\u002F04.tcp-socket-programming",{"title":4005,"path":4006,"stem":4007},"Протокол UDP та датаграмна передача даних","\u002Fclient-server-architecture\u002Fudp-datagram-transmission","17.client-server-architecture\u002F05.udp-datagram-transmission",{"title":4009,"path":4010,"stem":4011},"Криптографічний захист каналів зв","\u002Fclient-server-architecture\u002Ftls-cryptographic-channel-protection","17.client-server-architecture\u002F06.tls-cryptographic-channel-protection",{"title":4013,"path":4014,"stem":4015},"Прикладні мережеві протоколи DNS та SMTP","\u002Fclient-server-architecture\u002Fdns-smtp-application-protocols","17.client-server-architecture\u002F07.dns-smtp-application-protocols",{"title":4017,"path":4018,"stem":4019},"Протокол HTTP та стандартний вебсервер Go","\u002Fclient-server-architecture\u002Fhttp-protocol-and-web-server","17.client-server-architecture\u002F08.http-protocol-and-web-server",{"title":4021,"path":4022,"stem":4023},"Проєктування REST API та маршрутизація засобами Chi","\u002Fclient-server-architecture\u002Frest-api-design-and-chi-routing","17.client-server-architecture\u002F09.rest-api-design-and-chi-routing",{"title":4025,"path":4026,"stem":4027},"Лекція 10. Шарувата архітектура та конвеєри обробки запитів","\u002Fclient-server-architecture\u002Flayered-architecture-and-middleware-pipelines","17.client-server-architecture\u002F10.layered-architecture-and-middleware-pipelines",{"title":4029,"path":4030,"stem":4031},"Лекція 11. Архітектура та маршрутизація фреймворку Gin","\u002Fclient-server-architecture\u002Fgin-framework-architecture-and-routing","17.client-server-architecture\u002F11.gin-framework-architecture-and-routing",{"title":4033,"path":4034,"stem":4035},"Лекція 12. Валідація даних, безпека та документування у Gin","\u002Fclient-server-architecture\u002Fdata-validation-security-and-documentation-in-gin","17.client-server-architecture\u002F12.data-validation-security-and-documentation-in-gin",{"title":4037,"path":4038,"stem":4039},"Лекція 13. Взаємодія з реляційними СУБД та технології ORM","\u002Fclient-server-architecture\u002Frelational-databases-and-orm","17.client-server-architecture\u002F13.relational-databases-and-orm",{"title":4041,"path":4042,"stem":4043},"Потокова передача даних та протокол WebSocket","\u002Fclient-server-architecture\u002Fstreaming-data-and-websockets","17.client-server-architecture\u002F14.streaming-data-and-websockets",{"title":4045,"path":4046,"stem":4047},"Міжсервісна взаємодія на основі протоколу gRPC","\u002Fclient-server-architecture\u002Fgrpc-inter-service-communication","17.client-server-architecture\u002F15.grpc-inter-service-communication",{"title":4049,"path":4050,"stem":4051},"Асинхронний обмін повідомленнями та протоколи черг","\u002Fclient-server-architecture\u002Fasynchronous-message-exchange-and-queue-protocols","17.client-server-architecture\u002F16.asynchronous-message-exchange-and-queue-protocols",{"title":4053,"path":4054,"stem":4055},"Протоколи потокового медіа та технологія WebRTC","\u002Fclient-server-architecture\u002Fstreaming-media-protocols-and-webrtc","17.client-server-architecture\u002F17.streaming-media-protocols-and-webrtc",{"title":4057,"path":4058,"stem":4059},"Моніторинг, безпека та контейнеризація застосунків","\u002Fclient-server-architecture\u002Fmonitoring-security-and-containerization","17.client-server-architecture\u002F18.monitoring-security-and-containerization",{"title":4061,"icon":2118,"path":4062,"stem":4063,"children":4064,"page":59},"Розробка WEB-застосувань","\u002Fweb-development","18.web-development",[4065,4110,4148,4177,4206,4263,4313,4407,4473,4555,4588,4616,4658,4704,4747],{"title":4066,"icon":658,"path":4067,"stem":4068,"children":4069,"page":59},"Архітектура комп'ютерних мереж та мережеві протоколи","\u002Fweb-development\u002Fnetwork-architecture-and-protocols","18.web-development\u002F01.network-architecture-and-protocols",[4070,4074,4078,4082,4086,4090,4094,4098,4102,4106],{"title":4071,"path":4072,"stem":4073},"Еволюція веброзробки та модель клієнт-сервер","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fweb-evolution-client-server","18.web-development\u002F01.network-architecture-and-protocols\u002F01.web-evolution-client-server",{"title":4075,"path":4076,"stem":4077},"Еталонна модель OSI","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fosi-model","18.web-development\u002F01.network-architecture-and-protocols\u002F02.osi-model",{"title":4079,"path":4080,"stem":4081},"Стек протоколів TCP\u002FIP","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Ftcp-ip-stack","18.web-development\u002F01.network-architecture-and-protocols\u002F03.tcp-ip-stack",{"title":4083,"path":4084,"stem":4085},"IP-адресація, порти та сокети","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fip-addressing-ports-sockets","18.web-development\u002F01.network-architecture-and-protocols\u002F04.ip-addressing-ports-sockets",{"title":4087,"path":4088,"stem":4089},"Протоколи TCP та UDP","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Ftcp-udp-protocols","18.web-development\u002F01.network-architecture-and-protocols\u002F05.tcp-udp-protocols",{"title":4091,"path":4092,"stem":4093},"Система доменних імен DNS","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fdns-system","18.web-development\u002F01.network-architecture-and-protocols\u002F06.dns-system",{"title":4095,"path":4096,"stem":4097},"Протокол SMTP та поштові протоколи","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fsmtp-email-protocols","18.web-development\u002F01.network-architecture-and-protocols\u002F07.smtp-email-protocols",{"title":4099,"path":4100,"stem":4101},"Протокол WebSocket","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fwebsocket-protocol","18.web-development\u002F01.network-architecture-and-protocols\u002F08.websocket-protocol",{"title":4103,"path":4104,"stem":4105},"Протокол SSH","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fssh-protocol","18.web-development\u002F01.network-architecture-and-protocols\u002F09.ssh-protocol",{"title":4107,"path":4108,"stem":4109},"Огляд сучасних серверних технологій","\u002Fweb-development\u002Fnetwork-architecture-and-protocols\u002Fmodern-backend-technologies","18.web-development\u002F01.network-architecture-and-protocols\u002F10.modern-backend-technologies",{"title":4111,"icon":4112,"path":4113,"stem":4114,"children":4115,"page":59},"Протокол HTTP та захищена передача даних HTTPS","i-lucide-lock","\u002Fweb-development\u002Fhttp-and-https","18.web-development\u002F02.http-and-https",[4116,4120,4124,4128,4132,4136,4140,4144],{"title":4117,"path":4118,"stem":4119},"Вступ до протоколу HTTP та еволюція веб-технологій","\u002Fweb-development\u002Fhttp-and-https\u002Fintroduction-and-evolution","18.web-development\u002F02.http-and-https\u002F01.introduction-and-evolution",{"title":4121,"path":4122,"stem":4123},"Анатомія HTTP-запиту","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-request-structure","18.web-development\u002F02.http-and-https\u002F02.http-request-structure",{"title":4125,"path":4126,"stem":4127},"Анатомія HTTP-відповіді","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-response-structure","18.web-development\u002F02.http-and-https\u002F03.http-response-structure",{"title":4129,"path":4130,"stem":4131},"Методи HTTP та їхня семантика","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-methods","18.web-development\u002F02.http-and-https\u002F04.http-methods",{"title":4133,"path":4134,"stem":4135},"Коди статусу HTTP та їхнє призначення","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-status-codes","18.web-development\u002F02.http-and-https\u002F05.http-status-codes",{"title":4137,"path":4138,"stem":4139},"HTTP-заголовки та їхнє призначення","\u002Fweb-development\u002Fhttp-and-https\u002Fhttp-headers","18.web-development\u002F02.http-and-https\u002F06.http-headers",{"title":4141,"path":4142,"stem":4143},"CORS та механізми безпеки HTTP","\u002Fweb-development\u002Fhttp-and-https\u002Fcors-and-security","18.web-development\u002F02.http-and-https\u002F07.cors-and-security",{"title":4145,"path":4146,"stem":4147},"HTTPS та криптографічні основи TLS","\u002Fweb-development\u002Fhttp-and-https\u002Fhttps-and-tls","18.web-development\u002F02.http-and-https\u002F08.https-and-tls",{"title":4149,"icon":2498,"path":4150,"stem":4151,"children":4152,"page":59},"Система типів мови TypeScript","\u002Fweb-development\u002Ftypescript-type-system","18.web-development\u002F03.typescript-type-system",[4153,4157,4161,4165,4169,4173],{"title":4154,"path":4155,"stem":4156},"Знайомство з TypeScript Compiler","\u002Fweb-development\u002Ftypescript-type-system\u002Fintroduction-compiler-config","18.web-development\u002F03.typescript-type-system\u002F01.introduction-compiler-config",{"title":4158,"path":4159,"stem":4160},"Примітивні типи даних: string, number, boolean","\u002Fweb-development\u002Ftypescript-type-system\u002Fprimitive-special-types","18.web-development\u002F03.typescript-type-system\u002F02.primitive-special-types",{"title":4162,"path":4163,"stem":4164},"Масиви, кортежі та об","\u002Fweb-development\u002Ftypescript-type-system\u002Farrays-tuples-objects","18.web-development\u002F03.typescript-type-system\u002F03.arrays-tuples-objects",{"title":4166,"path":4167,"stem":4168},"Псевдоніми типів, Union типи, Intersection типи та літеральні типи","\u002Fweb-development\u002Ftypescript-type-system\u002Funion-intersection-literals","18.web-development\u002F03.typescript-type-system\u002F04.union-intersection-literals",{"title":4170,"path":4171,"stem":4172},"Type Narrowing (звуження типів)","\u002Fweb-development\u002Ftypescript-type-system\u002Ftype-narrowing","18.web-development\u002F03.typescript-type-system\u002F05.type-narrowing",{"title":4174,"path":4175,"stem":4176},"Типізація функцій","\u002Fweb-development\u002Ftypescript-type-system\u002Ffunctions-typing","18.web-development\u002F03.typescript-type-system\u002F06.functions-typing",{"title":4178,"icon":2498,"path":4179,"stem":4180,"children":4181,"page":59},"Розширені засоби мови TypeScript","\u002Fweb-development\u002Ftypescript-advanced","18.web-development\u002F04.typescript-advanced",[4182,4186,4190,4194,4198,4202],{"title":4183,"path":4184,"stem":4185},"Interfaces та Classes","\u002Fweb-development\u002Ftypescript-advanced\u002Finterfaces-classes","18.web-development\u002F04.typescript-advanced\u002F01.interfaces-classes",{"title":4187,"path":4188,"stem":4189},"Основи Generic типів","\u002Fweb-development\u002Ftypescript-advanced\u002Fgenerics-fundamentals","18.web-development\u002F04.typescript-advanced\u002F02.generics-fundamentals",{"title":4191,"path":4192,"stem":4193},"Enums та Decorators","\u002Fweb-development\u002Ftypescript-advanced\u002Fenums-decorators","18.web-development\u002F04.typescript-advanced\u002F03.enums-decorators",{"title":4195,"path":4196,"stem":4197},"Utility Types","\u002Fweb-development\u002Ftypescript-advanced\u002Futility-types","18.web-development\u002F04.typescript-advanced\u002F04.utility-types",{"title":4199,"path":4200,"stem":4201},"Mapped та Conditional Types","\u002Fweb-development\u002Ftypescript-advanced\u002Fmapped-conditional-types","18.web-development\u002F04.typescript-advanced\u002F05.mapped-conditional-types",{"title":4203,"path":4204,"stem":4205},"Модулі та Strict Mode","\u002Fweb-development\u002Ftypescript-advanced\u002Fmodules-strict-mode","18.web-development\u002F04.typescript-advanced\u002F06.modules-strict-mode",{"title":4207,"icon":4208,"path":4209,"stem":4210,"children":4211,"page":59},"Платформа Node.js як середовище виконання","i-devicon-nodejs","\u002Fweb-development\u002Fnodejs-platform","18.web-development\u002F05.nodejs-platform",[4212,4216,4220,4224,4228,4232,4236,4240,4244,4248,4252,4255,4259],{"title":4213,"path":4214,"stem":4215},"Що таке Node.js та його архітектура","\u002Fweb-development\u002Fnodejs-platform\u002Fwhat-is-nodejs","18.web-development\u002F05.nodejs-platform\u002F01.what-is-nodejs",{"title":4217,"path":4218,"stem":4219},"Event Loop — серце Node.js","\u002Fweb-development\u002Fnodejs-platform\u002Fevent-loop","18.web-development\u002F05.nodejs-platform\u002F02.event-loop",{"title":4221,"path":4222,"stem":4223},"Модульні системи: CommonJS vs ES Modules","\u002Fweb-development\u002Fnodejs-platform\u002Fmodule-systems","18.web-development\u002F05.nodejs-platform\u002F03.module-systems",{"title":4225,"path":4226,"stem":4227},"npm та package.json","\u002Fweb-development\u002Fnodejs-platform\u002Fnpm-package-json","18.web-development\u002F05.nodejs-platform\u002F04.npm-package-json",{"title":4229,"path":4230,"stem":4231},"Глобальні об\\","\u002Fweb-development\u002Fnodejs-platform\u002Fglobal-objects","18.web-development\u002F05.nodejs-platform\u002F05.global-objects",{"title":4233,"path":4234,"stem":4235},"Вбудований модуль fs (File System)","\u002Fweb-development\u002Fnodejs-platform\u002Fbuilt-in-modules-fs","18.web-development\u002F05.nodejs-platform\u002F06.built-in-modules-fs",{"title":4237,"path":4238,"stem":4239},"Вбудований модуль path","\u002Fweb-development\u002Fnodejs-platform\u002Fbuilt-in-modules-path","18.web-development\u002F05.nodejs-platform\u002F07.built-in-modules-path",{"title":4241,"path":4242,"stem":4243},"Вбудований модуль http","\u002Fweb-development\u002Fnodejs-platform\u002Fbuilt-in-modules-http","18.web-development\u002F05.nodejs-platform\u002F08.built-in-modules-http",{"title":4245,"path":4246,"stem":4247},"Streams — потоки даних у Node.js","\u002Fweb-development\u002Fnodejs-platform\u002Fstreams","18.web-development\u002F05.nodejs-platform\u002F09.streams",{"title":4249,"path":4250,"stem":4251},"Вбудований модуль util (Utilities)","\u002Fweb-development\u002Fnodejs-platform\u002Fbuilt-in-modules-util","18.web-development\u002F05.nodejs-platform\u002F10.built-in-modules-util",{"title":3141,"path":4253,"stem":4254},"\u002Fweb-development\u002Fnodejs-platform\u002Fenvironment-variables","18.web-development\u002F05.nodejs-platform\u002F11.environment-variables",{"title":4256,"path":4257,"stem":4258},"Інструменти налагодження та розробки","\u002Fweb-development\u002Fnodejs-platform\u002Fdebugging-tools","18.web-development\u002F05.nodejs-platform\u002F12.debugging-tools",{"title":4260,"path":4261,"stem":4262},"Продуктивність та Best Practices","\u002Fweb-development\u002Fnodejs-platform\u002Fperformance-best-practices","18.web-development\u002F05.nodejs-platform\u002F13.performance-best-practices",{"title":4264,"icon":4265,"path":4266,"stem":4267,"children":4268,"page":59},"Асинхронне програмування у Node.js","i-lucide-clock","\u002Fweb-development\u002Fasync-programming-nodejs","18.web-development\u002F06.async-programming-nodejs",[4269,4273,4277,4281,4285,4289,4293,4297,4301,4305,4309],{"title":4270,"path":4271,"stem":4272},"Синхронний vs асинхронний код","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fsync-vs-async","18.web-development\u002F06.async-programming-nodejs\u002F01.sync-vs-async",{"title":4274,"path":4275,"stem":4276},"Callbacks — перша парадигма асинхронності","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fcallbacks","18.web-development\u002F06.async-programming-nodejs\u002F02.callbacks",{"title":4278,"path":4279,"stem":4280},"Promises — стандарт асинхронності","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fpromises","18.web-development\u002F06.async-programming-nodejs\u002F03.promises",{"title":4282,"path":4283,"stem":4284},"Promise комбінатори","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fpromise-combinators","18.web-development\u002F06.async-programming-nodejs\u002F04.promise-combinators",{"title":4286,"path":4287,"stem":4288},"Async\u002FAwait — синтаксичний цукор над Promises","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fasync-await","18.web-development\u002F06.async-programming-nodejs\u002F05.async-await",{"title":4290,"path":4291,"stem":4292},"Паралельне vs послідовне виконання","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fparallel-vs-sequential","18.web-development\u002F06.async-programming-nodejs\u002F06.parallel-vs-sequential",{"title":4294,"path":4295,"stem":4296},"EventEmitter — подієва модель Node.js","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fevent-emitter","18.web-development\u002F06.async-programming-nodejs\u002F07.event-emitter",{"title":4298,"path":4299,"stem":4300},"Worker Threads — багатопотоковість у Node.js","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fworker-threads","18.web-development\u002F06.async-programming-nodejs\u002F08.worker-threads",{"title":4302,"path":4303,"stem":4304},"Child Processes — запуск зовнішніх процесів","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fchild-processes","18.web-development\u002F06.async-programming-nodejs\u002F09.child-processes",{"title":4306,"path":4307,"stem":4308},"Обробка помилок у асинхронному коді","\u002Fweb-development\u002Fasync-programming-nodejs\u002Ferror-handling","18.web-development\u002F06.async-programming-nodejs\u002F10.error-handling",{"title":4310,"path":4311,"stem":4312},"Порівняння Node.js фреймворків","\u002Fweb-development\u002Fasync-programming-nodejs\u002Fnodejs-frameworks","18.web-development\u002F06.async-programming-nodejs\u002F11.nodejs-frameworks",{"title":4314,"icon":4315,"path":4316,"stem":4317,"children":4318,"page":59},"Тема 7. Фреймворк NestJS. Контролери та маршрутизація","i-lucide-route","\u002Fweb-development\u002Fnestjs-controllers-routing","18.web-development\u002F07.nestjs-controllers-routing",[4319,4323,4327,4331,4335,4339,4343,4347,4351,4355,4359,4363,4367,4371,4375,4379,4383,4387,4391,4395,4399,4403],{"title":4320,"path":4321,"stem":4322},"Філософія та архітектура NestJS","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fnestjs-philosophy","18.web-development\u002F07.nestjs-controllers-routing\u002F01.nestjs-philosophy",{"title":4324,"path":4325,"stem":4326},"Три стовпи архітектури: модулі, контролери, провайдери","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fthree-pillars","18.web-development\u002F07.nestjs-controllers-routing\u002F02.three-pillars",{"title":4328,"path":4329,"stem":4330},"Інверсія керування (IoC) та ін","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fioc-and-di","18.web-development\u002F07.nestjs-controllers-routing\u002F03.ioc-and-di",{"title":4332,"path":4333,"stem":4334},"Встановлення NestJS CLI та створення проєкту","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fnestjs-cli-setup","18.web-development\u002F07.nestjs-controllers-routing\u002F04.nestjs-cli-setup",{"title":4336,"path":4337,"stem":4338},"Структура проєкту NestJS та конвенції","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fproject-structure","18.web-development\u002F07.nestjs-controllers-routing\u002F05.project-structure",{"title":4340,"path":4341,"stem":4342},"Контролери: основи маршрутизації","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fcontrollers-basics","18.web-development\u002F07.nestjs-controllers-routing\u002F06.controllers-basics",{"title":4344,"path":4345,"stem":4346},"Декоратори маршрутизації HTTP-методів","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Froute-decorators","18.web-development\u002F07.nestjs-controllers-routing\u002F07.route-decorators",{"title":4348,"path":4349,"stem":4350},"Параметри маршруту та декоратор @Param","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Froute-parameters","18.web-development\u002F07.nestjs-controllers-routing\u002F08.route-parameters",{"title":4352,"path":4353,"stem":4354},"Query-параметри та декоратор @Query","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fquery-parameters","18.web-development\u002F07.nestjs-controllers-routing\u002F09.query-parameters",{"title":4356,"path":4357,"stem":4358},"Тіло запиту та декоратор @Body","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Frequest-body","18.web-development\u002F07.nestjs-controllers-routing\u002F10.request-body",{"title":4360,"path":4361,"stem":4362},"Робота з заголовками запиту та відповіді","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fheaders-decorators","18.web-development\u002F07.nestjs-controllers-routing\u002F11.headers-decorators",{"title":4364,"path":4365,"stem":4366},"Data Transfer Objects (DTO): визначення та призначення","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fdto-introduction","18.web-development\u002F07.nestjs-controllers-routing\u002F12.dto-introduction",{"title":4368,"path":4369,"stem":4370},"Об'єкти Request та Response","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Frequest-response-objects","18.web-development\u002F07.nestjs-controllers-routing\u002F13.request-response-objects",{"title":4372,"path":4373,"stem":4374},"HTTP-статус коди та декоратор @HttpCode","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fhttp-status-codes","18.web-development\u002F07.nestjs-controllers-routing\u002F14.http-status-codes",{"title":4376,"path":4377,"stem":4378},"Перенаправлення (Redirects) та декоратор @Redirect","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fredirects","18.web-development\u002F07.nestjs-controllers-routing\u002F15.redirects",{"title":4380,"path":4381,"stem":4382},"Асинхронні обробники та Observables","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fasync-handlers","18.web-development\u002F07.nestjs-controllers-routing\u002F16.async-handlers",{"title":4384,"path":4385,"stem":4386},"Маршрутизація за піддоменами та версіонування API","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fsub-domain-routing","18.web-development\u002F07.nestjs-controllers-routing\u002F17.sub-domain-routing",{"title":4388,"path":4389,"stem":4390},"Області видимості контролерів (Scopes)","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fcontroller-scopes","18.web-development\u002F07.nestjs-controllers-routing\u002F18.controller-scopes",{"title":4392,"path":4393,"stem":4394},"Основні команди NestJS CLI","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fnestjs-cli-commands","18.web-development\u002F07.nestjs-controllers-routing\u002F19.nestjs-cli-commands",{"title":4396,"path":4397,"stem":4398},"Практичний приклад: створення CRUD-контролера","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fcrud-example","18.web-development\u002F07.nestjs-controllers-routing\u002F20.crud-example",{"title":4400,"path":4401,"stem":4402},"Тестування контролерів","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Ftesting-controllers","18.web-development\u002F07.nestjs-controllers-routing\u002F21.testing-controllers",{"title":4404,"path":4405,"stem":4406},"Best Practices для контролерів та маршрутизації","\u002Fweb-development\u002Fnestjs-controllers-routing\u002Fbest-practices","18.web-development\u002F07.nestjs-controllers-routing\u002F22.best-practices",{"title":4408,"icon":4409,"path":4410,"stem":4411,"children":4412,"page":59},"Тема 8. Провайдери, сервіси та модульна система NestJS","i-lucide-package","\u002Fweb-development\u002Fnestjs-providers-modules","18.web-development\u002F08.nestjs-providers-modules",[4413,4417,4421,4425,4429,4433,4437,4441,4445,4449,4453,4457,4461,4465,4469],{"title":4414,"path":4415,"stem":4416},"Концепція провайдерів у NestJS","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fproviders-concept","18.web-development\u002F08.nestjs-providers-modules\u002F01.providers-concept",{"title":4418,"path":4419,"stem":4420},"Сервіси: інкапсуляція бізнес-логіки","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fservices-business-logic","18.web-development\u002F08.nestjs-providers-modules\u002F02.services-business-logic",{"title":4422,"path":4423,"stem":4424},"Практика ін","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fdependency-injection-practice","18.web-development\u002F08.nestjs-providers-modules\u002F03.dependency-injection-practice",{"title":4426,"path":4427,"stem":4428},"Custom Providers: useValue, useClass, useFactory, useExisting","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fcustom-providers","18.web-development\u002F08.nestjs-providers-modules\u002F04.custom-providers",{"title":4430,"path":4431,"stem":4432},"Injection Tokens: рядкові та символьні токени","\u002Fweb-development\u002Fnestjs-providers-modules\u002Finjection-tokens","18.web-development\u002F08.nestjs-providers-modules\u002F05.injection-tokens",{"title":4434,"path":4435,"stem":4436},"Опціональні провайдери та декоратор @Optional","\u002Fweb-development\u002Fnestjs-providers-modules\u002Foptional-providers","18.web-development\u002F08.nestjs-providers-modules\u002F07.optional-providers",{"title":4438,"path":4439,"stem":4440},"Структура модулів: imports, controllers, providers, exports","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fmodules-structure","18.web-development\u002F08.nestjs-providers-modules\u002F08.modules-structure",{"title":4442,"path":4443,"stem":4444},"Feature Modules: ізоляція функціональності","\u002Fweb-development\u002Fnestjs-providers-modules\u002Ffeature-modules","18.web-development\u002F08.nestjs-providers-modules\u002F09.feature-modules",{"title":4446,"path":4447,"stem":4448},"Shared Modules: перевикористання провайдерів","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fshared-modules","18.web-development\u002F08.nestjs-providers-modules\u002F10.shared-modules",{"title":4450,"path":4451,"stem":4452},"Global Modules та декоратор @Global","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fglobal-modules","18.web-development\u002F08.nestjs-providers-modules\u002F11.global-modules",{"title":4454,"path":4455,"stem":4456},"Dynamic Modules: конфігурація під час виконання","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fdynamic-modules","18.web-development\u002F08.nestjs-providers-modules\u002F12.dynamic-modules",{"title":4458,"path":4459,"stem":4460},"Циклічні залежності та forwardRef","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fcircular-dependencies","18.web-development\u002F08.nestjs-providers-modules\u002F13.circular-dependencies",{"title":4462,"path":4463,"stem":4464},"Lifecycle Hooks: хуки життєвого циклу","\u002Fweb-development\u002Fnestjs-providers-modules\u002Flifecycle-hooks","18.web-development\u002F08.nestjs-providers-modules\u002F14.lifecycle-hooks",{"title":4466,"path":4467,"stem":4468},"ModuleRef: динамічний доступ до провайдерів","\u002Fweb-development\u002Fnestjs-providers-modules\u002Fmodule-reference","18.web-development\u002F08.nestjs-providers-modules\u002F15.module-reference",{"title":4470,"path":4471,"stem":4472},"Тестування сервісів та провайдерів","\u002Fweb-development\u002Fnestjs-providers-modules\u002Ftesting-services","18.web-development\u002F08.nestjs-providers-modules\u002F16.testing-services",{"title":4474,"icon":4475,"path":4476,"stem":4477,"children":4478,"page":59},"Тема 9. Валідація даних та конвеєр обробки запитів у NestJS","i-lucide-filter","\u002Fweb-development\u002Fnestjs-validation-pipeline","18.web-development\u002F09.nestjs-validation-pipeline",[4479,4483,4487,4491,4495,4499,4503,4507,4511,4515,4519,4523,4527,4531,4535,4539,4543,4547,4551],{"title":4480,"path":4481,"stem":4482},"Request Pipeline: конвеєр обробки запитів","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Frequest-pipeline","18.web-development\u002F09.nestjs-validation-pipeline\u002F01.request-pipeline",{"title":4484,"path":4485,"stem":4486},"Pipes: концепція трансформації та валідації","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fpipes-concept","18.web-development\u002F09.nestjs-validation-pipeline\u002F02.pipes-concept",{"title":4488,"path":4489,"stem":4490},"Вбудовані pipes: ParseIntPipe, ParseBoolPipe, ParseUUIDPipe","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fbuilt-in-pipes","18.web-development\u002F09.nestjs-validation-pipeline\u002F03.built-in-pipes",{"title":4492,"path":4493,"stem":4494},"ValidationPipe: автоматична валідація DTO","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fvalidation-pipe","18.web-development\u002F09.nestjs-validation-pipeline\u002F04.validation-pipe",{"title":4496,"path":4497,"stem":4498},"Декоратори class-validator для DTO","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fclass-validator-decorators","18.web-development\u002F09.nestjs-validation-pipeline\u002F05.class-validator-decorators",{"title":4500,"path":4501,"stem":4502},"Створення кастомних валідаторів","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fcustom-validators","18.web-development\u002F09.nestjs-validation-pipeline\u002F06.custom-validators",{"title":4504,"path":4505,"stem":4506},"Створення власних Pipes","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fcustom-pipes","18.web-development\u002F09.nestjs-validation-pipeline\u002F07.custom-pipes",{"title":4508,"path":4509,"stem":4510},"Основи Middleware","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fmiddleware-basics","18.web-development\u002F09.nestjs-validation-pipeline\u002F08.middleware-basics",{"title":4512,"path":4513,"stem":4514},"Практичні приклади Middleware","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fmiddleware-examples","18.web-development\u002F09.nestjs-validation-pipeline\u002F09.middleware-examples",{"title":4516,"path":4517,"stem":4518},"Guards: захисні перехоплювачі доступу","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fguards-concept","18.web-development\u002F09.nestjs-validation-pipeline\u002F10.guards-concept",{"title":4520,"path":4521,"stem":4522},"Створення кастомних Guards","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fcustom-guards","18.web-development\u002F09.nestjs-validation-pipeline\u002F11.custom-guards",{"title":4524,"path":4525,"stem":4526},"Interceptors: перехоплення та трансформація відповідей","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Finterceptors-concept","18.web-development\u002F09.nestjs-validation-pipeline\u002F12.interceptors-concept",{"title":4528,"path":4529,"stem":4530},"Практичні приклади Interceptors","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Finterceptor-examples","18.web-development\u002F09.nestjs-validation-pipeline\u002F13.interceptor-examples",{"title":4532,"path":4533,"stem":4534},"Exception Filters: обробка виключень","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fexception-filters","18.web-development\u002F09.nestjs-validation-pipeline\u002F14.exception-filters",{"title":4536,"path":4537,"stem":4538},"Custom Exception Filters: спеціалізовані фільтри","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fcustom-exception-filters","18.web-development\u002F09.nestjs-validation-pipeline\u002F15.custom-exception-filters",{"title":4540,"path":4541,"stem":4542},"Execution Context: контекст виконання","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fexecution-context","18.web-development\u002F09.nestjs-validation-pipeline\u002F16.execution-context",{"title":4544,"path":4545,"stem":4546},"Custom Decorators: власні декоратори","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fcustom-decorators","18.web-development\u002F09.nestjs-validation-pipeline\u002F17.custom-decorators",{"title":4548,"path":4549,"stem":4550},"Global Components: глобальні компоненти","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fglobal-components","18.web-development\u002F09.nestjs-validation-pipeline\u002F18.global-components",{"title":4552,"path":4553,"stem":4554},"Pipeline Best Practices: найкращі практики","\u002Fweb-development\u002Fnestjs-validation-pipeline\u002Fpipeline-best-practices","18.web-development\u002F09.nestjs-validation-pipeline\u002F19.pipeline-best-practices",{"title":4556,"icon":2865,"path":4557,"stem":4558,"children":4559,"page":59},"Інтеграція реляційних баз даних засобами TypeORM","\u002Fweb-development\u002Ftypeorm-integration","18.web-development\u002F10.typeorm-integration",[4560,4564,4568,4572,4576,4580,4584],{"title":4561,"path":4562,"stem":4563},"Робота з базами даних у Node.js через нативні драйвери","\u002Fweb-development\u002Ftypeorm-integration\u002F01-nodejs-database-drivers","18.web-development\u002F10.typeorm-integration\u002F01-nodejs-database-drivers",{"title":4565,"path":4566,"stem":4567},"ORM та знайомство з TypeORM","\u002Fweb-development\u002Ftypeorm-integration\u002F02-orm-and-typeorm-intro","18.web-development\u002F10.typeorm-integration\u002F02-orm-and-typeorm-intro",{"title":4569,"path":4570,"stem":4571},"Налаштування TypeORM у NestJS проєкті","\u002Fweb-development\u002Ftypeorm-integration\u002F03-typeorm-setup-nestjs","18.web-development\u002F10.typeorm-integration\u002F03-typeorm-setup-nestjs",{"title":4573,"path":4574,"stem":4575},"Entity класи та декоратори TypeORM","\u002Fweb-development\u002Ftypeorm-integration\u002F04-entities-and-decorators","18.web-development\u002F10.typeorm-integration\u002F04-entities-and-decorators",{"title":4577,"path":4578,"stem":4579},"Repository Pattern та CRUD операції","\u002Fweb-development\u002Ftypeorm-integration\u002F05-repository-pattern-crud","18.web-development\u002F10.typeorm-integration\u002F05-repository-pattern-crud",{"title":4581,"path":4582,"stem":4583},"Пагінація та фільтрація даних","\u002Fweb-development\u002Ftypeorm-integration\u002F06-pagination-filtering","18.web-development\u002F10.typeorm-integration\u002F06-pagination-filtering",{"title":4585,"path":4586,"stem":4587},"QueryBuilder для складних запитів","\u002Fweb-development\u002Ftypeorm-integration\u002F07-query-builder","18.web-development\u002F10.typeorm-integration\u002F07-query-builder",{"title":4589,"icon":4590,"path":4591,"stem":4592,"children":4593,"page":59},"Зв'язки між сутностями, міграції та оптимізація запитів","i-lucide-git-branch","\u002Fweb-development\u002Fentity-relations-migrations","18.web-development\u002F11.entity-relations-migrations",[4594,4598,4602,4606,4610],{"title":4595,"path":4596,"stem":4597},"Зв'язки One-to-Many та Many-to-One","\u002Fweb-development\u002Fentity-relations-migrations\u002F01-one-to-many-many-to-one","18.web-development\u002F11.entity-relations-migrations\u002F01-one-to-many-many-to-one",{"title":4599,"path":4600,"stem":4601},"Зв'язки One-to-One та Many-to-Many","\u002Fweb-development\u002Fentity-relations-migrations\u002F02-one-to-one-many-to-many","18.web-development\u002F11.entity-relations-migrations\u002F02-one-to-one-many-to-many",{"title":4603,"path":4604,"stem":4605},"Керування міграціями бази даних","\u002Fweb-development\u002Fentity-relations-migrations\u002F03-migrations-management","18.web-development\u002F11.entity-relations-migrations\u002F03-migrations-management",{"title":4607,"path":4608,"stem":4609},"Транзакції у TypeORM","\u002Fweb-development\u002Fentity-relations-migrations\u002F04-transactions","18.web-development\u002F11.entity-relations-migrations\u002F04-transactions",{"title":4611,"path":4612,"stem":4613,"children":4614},"Індекси та оптимізація запитів","\u002Fweb-development\u002Fentity-relations-migrations\u002F05-indexes-optimization","18.web-development\u002F11.entity-relations-migrations\u002F05-indexes-optimization",[4615],{"title":4611,"path":4612,"stem":4613},{"title":4617,"icon":4618,"path":4619,"stem":4620,"children":4621,"page":59},"Механізми автентифікації у вебзастосунках","i-lucide-key","\u002Fweb-development\u002Fauthentication","18.web-development\u002F12.authentication",[4622,4626,4630,4634,4638,4642,4646,4650,4654],{"title":4623,"path":4624,"stem":4625},"Автентифікація vs Авторизація","\u002Fweb-development\u002Fauthentication\u002F01-authentication-vs-authorization","18.web-development\u002F12.authentication\u002F01-authentication-vs-authorization",{"title":4627,"path":4628,"stem":4629},"Стратегії автентифікації","\u002Fweb-development\u002Fauthentication\u002F02-authentication-strategies","18.web-development\u002F12.authentication\u002F02-authentication-strategies",{"title":4631,"path":4632,"stem":4633},"Автентифікація на основі сесій","\u002Fweb-development\u002Fauthentication\u002F03-session-based-authentication","18.web-development\u002F12.authentication\u002F03-session-based-authentication",{"title":4635,"path":4636,"stem":4637},"Структура JSON Web Token (JWT)","\u002Fweb-development\u002Fauthentication\u002F04-jwt-structure","18.web-development\u002F12.authentication\u002F04-jwt-structure",{"title":4639,"path":4640,"stem":4641},"Інтеграція JWT у NestJS","\u002Fweb-development\u002Fauthentication\u002F05-nestjs-jwt-integration","18.web-development\u002F12.authentication\u002F05-nestjs-jwt-integration",{"title":4643,"path":4644,"stem":4645},"Passport.js стратегії у NestJS","\u002Fweb-development\u002Fauthentication\u002F06-passport-strategies","18.web-development\u002F12.authentication\u002F06-passport-strategies",{"title":4647,"path":4648,"stem":4649},"Безпечне зберігання паролів: хешування та криптографічний захист","\u002Fweb-development\u002Fauthentication\u002F07-password-hashing","18.web-development\u002F12.authentication\u002F07-password-hashing",{"title":4651,"path":4652,"stem":4653},"Refresh Token та безпечне оновлення доступу","\u002Fweb-development\u002Fauthentication\u002F08-refresh-tokens","18.web-development\u002F12.authentication\u002F08-refresh-tokens",{"title":4655,"path":4656,"stem":4657},"OAuth 2.0 та OpenID Connect: делегована автентифікація","\u002Fweb-development\u002Fauthentication\u002F09-oauth2-openid","18.web-development\u002F12.authentication\u002F09-oauth2-openid",{"title":4659,"icon":4660,"path":4661,"stem":4662,"children":4663,"page":59},"Авторизація та безпека серверних застосунків","i-lucide-shield","\u002Fweb-development\u002Fauthorization-security","18.web-development\u002F13.authorization-security",[4664,4668,4672,4676,4680,4684,4688,4692,4696,4700],{"title":4665,"path":4666,"stem":4667},"Моделі авторизації","\u002Fweb-development\u002Fauthorization-security\u002F01-authorization-models","18.web-development\u002F13.authorization-security\u002F01-authorization-models",{"title":4669,"path":4670,"stem":4671},"Реалізація RBAC у NestJS","\u002Fweb-development\u002Fauthorization-security\u002F02-rbac-implementation","18.web-development\u002F13.authorization-security\u002F02-rbac-implementation",{"title":4673,"path":4674,"stem":4675},"Permission-Based Access Control","\u002Fweb-development\u002Fauthorization-security\u002F03-permission-based-access","18.web-development\u002F13.authorization-security\u002F03-permission-based-access",{"title":4677,"path":4678,"stem":4679},"ABAC та бібліотека CASL","\u002Fweb-development\u002Fauthorization-security\u002F04-abac-casl","18.web-development\u002F13.authorization-security\u002F04-abac-casl",{"title":4681,"path":4682,"stem":4683},"Кастомні декоратори та метадані","\u002Fweb-development\u002Fauthorization-security\u002F05-custom-decorators-metadata","18.web-development\u002F13.authorization-security\u002F05-custom-decorators-metadata",{"title":4685,"path":4686,"stem":4687},"Конфігурація CORS","\u002Fweb-development\u002Fauthorization-security\u002F06-cors-configuration","18.web-development\u002F13.authorization-security\u002F06-cors-configuration",{"title":4689,"path":4690,"stem":4691},"Захист через HTTP Security Headers (Helmet)","\u002Fweb-development\u002Fauthorization-security\u002F07-helmet-security-headers","18.web-development\u002F13.authorization-security\u002F07-helmet-security-headers",{"title":4693,"path":4694,"stem":4695},"Rate Limiting та Throttling","\u002Fweb-development\u002Fauthorization-security\u002F08-rate-limiting-throttling","18.web-development\u002F13.authorization-security\u002F08-rate-limiting-throttling",{"title":4697,"path":4698,"stem":4699},"Захист від поширених атак","\u002Fweb-development\u002Fauthorization-security\u002F09-common-attacks-protection","18.web-development\u002F13.authorization-security\u002F09-common-attacks-protection",{"title":4701,"path":4702,"stem":4703},"HTTPS у продакшені","\u002Fweb-development\u002Fauthorization-security\u002F10-https-production","18.web-development\u002F13.authorization-security\u002F10-https-production",{"title":4705,"icon":871,"path":4706,"stem":4707,"children":4708,"page":59},"Комунікація у реальному часі, нотифікації та фонові задачі","\u002Fweb-development\u002Frealtime-notifications-jobs","18.web-development\u002F14.realtime-notifications-jobs",[4709,4713,4717,4721,4725,4729,4732,4735,4739,4743],{"title":4710,"path":4711,"stem":4712},"Методи комунікації у реальному часі","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F01-realtime-communication-methods","18.web-development\u002F14.realtime-notifications-jobs\u002F01-realtime-communication-methods",{"title":4714,"path":4715,"stem":4716},"WebSocket Gateway у NestJS","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F02-websocket-gateway","18.web-development\u002F14.realtime-notifications-jobs\u002F02-websocket-gateway",{"title":4718,"path":4719,"stem":4720},"Lifecycle та Rooms у WebSocket","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F03-websocket-lifecycle-rooms","18.web-development\u002F14.realtime-notifications-jobs\u002F03-websocket-lifecycle-rooms",{"title":4722,"path":4723,"stem":4724},"Server-Sent Events (SSE)","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F04-server-sent-events","18.web-development\u002F14.realtime-notifications-jobs\u002F04-server-sent-events",{"title":4726,"path":4727,"stem":4728},"Архітектура системи нотифікацій","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F05-notification-system","18.web-development\u002F14.realtime-notifications-jobs\u002F05-notification-system",{"title":904,"path":4730,"stem":4731},"\u002Fweb-development\u002Frealtime-notifications-jobs\u002F06-email-notifications","18.web-development\u002F14.realtime-notifications-jobs\u002F06-email-notifications",{"title":900,"path":4733,"stem":4734},"\u002Fweb-development\u002Frealtime-notifications-jobs\u002F07-web-push-notifications","18.web-development\u002F14.realtime-notifications-jobs\u002F07-web-push-notifications",{"title":4736,"path":4737,"stem":4738},"Фонові задачі та черги","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F08-background-jobs-queues","18.web-development\u002F14.realtime-notifications-jobs\u002F08-background-jobs-queues",{"title":4740,"path":4741,"stem":4742},"Планувальники та Cron jobs","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F09-schedulers-cron","18.web-development\u002F14.realtime-notifications-jobs\u002F09-schedulers-cron",{"title":4744,"path":4745,"stem":4746},"Практичні сценарії застосування","\u002Fweb-development\u002Frealtime-notifications-jobs\u002F10-practical-scenarios","18.web-development\u002F14.realtime-notifications-jobs\u002F10-practical-scenarios",{"title":4748,"icon":4749,"path":4750,"stem":4751,"children":4752,"page":59},"Тестування, документування та розгортання застосунків","i-lucide-rocket","\u002Fweb-development\u002Ftesting-documentation-deployment","18.web-development\u002F15.testing-documentation-deployment",[4753,4757,4761,4765,4769,4773,4777,4781,4785,4789],{"title":4754,"path":4755,"stem":4756},"Піраміда тестування та стратегія","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F01-testing-pyramid-strategy","18.web-development\u002F15.testing-documentation-deployment\u002F01-testing-pyramid-strategy",{"title":4758,"path":4759,"stem":4760},"Основи Jest у NestJS","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F02-jest-nestjs-basics","18.web-development\u002F15.testing-documentation-deployment\u002F02-jest-nestjs-basics",{"title":4762,"path":4763,"stem":4764},"Unit-тестування сервісів","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F03-unit-testing-services","18.web-development\u002F15.testing-documentation-deployment\u002F03-unit-testing-services",{"title":4766,"path":4767,"stem":4768},"E2E тестування та покриття коду","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F04-e2e-testing-coverage","18.web-development\u002F15.testing-documentation-deployment\u002F04-e2e-testing-coverage",{"title":4770,"path":4771,"stem":4772},"Swagger, OpenAPI, Scalar та Arazzo","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F05-swagger-openapi","18.web-development\u002F15.testing-documentation-deployment\u002F05-swagger-openapi",{"title":4774,"path":4775,"stem":4776},"Керування конфігурацією","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F06-configuration-management","18.web-development\u002F15.testing-documentation-deployment\u002F06-configuration-management",{"title":4778,"path":4779,"stem":4780},"Docker та контейнеризація","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F07-docker-containerization","18.web-development\u002F15.testing-documentation-deployment\u002F07-docker-containerization",{"title":4782,"path":4783,"stem":4784},"CI\u002FCD Pipelines","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F08-cicd-pipelines","18.web-development\u002F15.testing-documentation-deployment\u002F08-cicd-pipelines",{"title":4786,"path":4787,"stem":4788},"Логування та моніторинг","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F09-logging-monitoring","18.web-development\u002F15.testing-documentation-deployment\u002F09-logging-monitoring",{"title":4790,"path":4791,"stem":4792},"Серіалізація та компресія","\u002Fweb-development\u002Ftesting-documentation-deployment\u002F10-serialization-compression","18.web-development\u002F15.testing-documentation-deployment\u002F10-serialization-compression",{"title":4794,"icon":4795,"path":4796,"stem":4797,"children":4798,"page":59},"AI Core: мислення AI","i-lucide-brain","\u002Fai-core","19.ai-core",[4799,4840,4889,4927],{"title":4800,"icon":4749,"path":4801,"stem":4802,"children":4803,"page":59},"Вхід у професію AI Native Developer","\u002Fai-core\u002Fai-native-developer","19.ai-core\u002F01.ai-native-developer",[4804,4808,4812,4816,4820,4824,4828,4832,4836],{"title":4805,"path":4806,"stem":4807},"Вступ до професії розробника цифрових продуктів","\u002Fai-core\u002Fai-native-developer\u002Fdigital-products","19.ai-core\u002F01.ai-native-developer\u002F01.digital-products",{"title":4809,"path":4810,"stem":4811},"Професія розробника програмного забезпечення","\u002Fai-core\u002Fai-native-developer\u002Fsoftware-developer","19.ai-core\u002F01.ai-native-developer\u002F02.software-developer",{"title":4813,"path":4814,"stem":4815},"Професія AI Native Developer","\u002Fai-core\u002Fai-native-developer\u002Fai-native-developer","19.ai-core\u002F01.ai-native-developer\u002F03.ai-native-developer",{"title":4817,"path":4818,"stem":4819},"Вплив AI на розробку програмного забезпечення","\u002Fai-core\u002Fai-native-developer\u002Fai-impact-on-development","19.ai-core\u002F01.ai-native-developer\u002F04.ai-impact-on-development",{"title":4821,"path":4822,"stem":4823},"Життєвий цикл цифрового продукту","\u002Fai-core\u002Fai-native-developer\u002Fproduct-lifecycle","19.ai-core\u002F01.ai-native-developer\u002F05.product-lifecycle",{"title":4825,"path":4826,"stem":4827},"Застосування AI на етапах створення цифрового продукту","\u002Fai-core\u002Fai-native-developer\u002Fai-in-product-creation","19.ai-core\u002F01.ai-native-developer\u002F06.ai-in-product-creation",{"title":4829,"path":4830,"stem":4831},"Розподіл завдань між людиною та AI","\u002Fai-core\u002Fai-native-developer\u002Fhuman-vs-ai-tasks","19.ai-core\u002F01.ai-native-developer\u002F07.human-vs-ai-tasks",{"title":4833,"path":4834,"stem":4835},"Компетенції AI Native Developer","\u002Fai-core\u002Fai-native-developer\u002Fcompetencies","19.ai-core\u002F01.ai-native-developer\u002F08.competencies",{"title":4837,"path":4838,"stem":4839},"Карта професійного розвитку AI Native Developer","\u002Fai-core\u002Fai-native-developer\u002Fcareer-roadmap","19.ai-core\u002F01.ai-native-developer\u002F09.career-roadmap",{"title":4841,"icon":255,"path":4842,"stem":4843,"children":4844,"page":59},"Штучний інтелект і машинне навчання","\u002Fai-core\u002Fai-and-machine-learning","19.ai-core\u002F02.ai-and-machine-learning",[4845,4849,4853,4857,4861,4865,4869,4873,4877,4881,4885],{"title":4846,"path":4847,"stem":4848},"Штучний інтелект","\u002Fai-core\u002Fai-and-machine-learning\u002Fwhat-is-ai","19.ai-core\u002F02.ai-and-machine-learning\u002F01.what-is-ai",{"title":4850,"path":4851,"stem":4852},"Відмінність AI від інших програмних систем","\u002Fai-core\u002Fai-and-machine-learning\u002Fai-vs-automation","19.ai-core\u002F02.ai-and-machine-learning\u002F02.ai-vs-automation",{"title":4854,"path":4855,"stem":4856},"Завдання штучного інтелекту","\u002Fai-core\u002Fai-and-machine-learning\u002Fai-tasks","19.ai-core\u002F02.ai-and-machine-learning\u002F03.ai-tasks",{"title":4858,"path":4859,"stem":4860},"Machine Learning","\u002Fai-core\u002Fai-and-machine-learning\u002Fmachine-learning","19.ai-core\u002F02.ai-and-machine-learning\u002F04.machine-learning",{"title":4862,"path":4863,"stem":4864},"Machine Learning Workflow","\u002Fai-core\u002Fai-and-machine-learning\u002Fml-workflow","19.ai-core\u002F02.ai-and-machine-learning\u002F05.ml-workflow",{"title":4866,"path":4867,"stem":4868},"Типи машинного навчання","\u002Fai-core\u002Fai-and-machine-learning\u002Fml-types","19.ai-core\u002F02.ai-and-machine-learning\u002F06.ml-types",{"title":4870,"path":4871,"stem":4872},"Generative AI","\u002Fai-core\u002Fai-and-machine-learning\u002Fgenerative-ai","19.ai-core\u002F02.ai-and-machine-learning\u002F07.generative-ai",{"title":4874,"path":4875,"stem":4876},"Типи контенту, створюваного AI","\u002Fai-core\u002Fai-and-machine-learning\u002Fai-content-types","19.ai-core\u002F02.ai-and-machine-learning\u002F08.ai-content-types",{"title":4878,"path":4879,"stem":4880},"Великі мовні моделі","\u002Fai-core\u002Fai-and-machine-learning\u002Flarge-language-models","19.ai-core\u002F02.ai-and-machine-learning\u002F09.large-language-models",{"title":4882,"path":4883,"stem":4884},"Можливості та обмеження Generative AI","\u002Fai-core\u002Fai-and-machine-learning\u002Fcapabilities-and-limitations","19.ai-core\u002F02.ai-and-machine-learning\u002F10.capabilities-and-limitations",{"title":4886,"path":4887,"stem":4888},"Сучасні AI-асистенти","\u002Fai-core\u002Fai-and-machine-learning\u002Fmodern-ai-assistants","19.ai-core\u002F02.ai-and-machine-learning\u002F11.modern-ai-assistants",{"title":4890,"icon":4891,"path":4892,"stem":4893,"children":4894,"page":59},"Постановка завдань для AI: принципи створення промптів","i-lucide-message-square-text","\u002Fai-core\u002Fprompt-engineering","19.ai-core\u002F03.prompt-engineering",[4895,4899,4903,4907,4911,4915,4919,4923],{"title":4896,"path":4897,"stem":4898},"Промпт як інструмент","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-as-tool","19.ai-core\u002F03.prompt-engineering\u002F01.prompt-as-tool",{"title":4900,"path":4901,"stem":4902},"Принципи створення промптів","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-principles","19.ai-core\u002F03.prompt-engineering\u002F02.prompt-principles",{"title":4904,"path":4905,"stem":4906},"Структура промпта","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-structure","19.ai-core\u002F03.prompt-engineering\u002F03.prompt-structure",{"title":4908,"path":4909,"stem":4910},"Види промптів за типом задачі","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-types","19.ai-core\u002F03.prompt-engineering\u002F04.prompt-types",{"title":4912,"path":4913,"stem":4914},"Прийоми посилення промпта","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-enhancement","19.ai-core\u002F03.prompt-engineering\u002F05.prompt-enhancement",{"title":4916,"path":4917,"stem":4918},"Розбір і покращення промпта","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-analysis","19.ai-core\u002F03.prompt-engineering\u002F06.prompt-analysis",{"title":4920,"path":4921,"stem":4922},"Типові помилки під час створення промптів","\u002Fai-core\u002Fprompt-engineering\u002Fcommon-mistakes","19.ai-core\u002F03.prompt-engineering\u002F07.common-mistakes",{"title":4924,"path":4925,"stem":4926},"Практикум: особиста бібліотека промптів","\u002Fai-core\u002Fprompt-engineering\u002Fprompt-library","19.ai-core\u002F03.prompt-engineering\u002F08.prompt-library",{"title":4928,"icon":793,"path":4929,"stem":4930,"children":4931,"page":59},"Перевірка результатів, безпека та AI Native Workflow","\u002Fai-core\u002Fverification-security-workflow","19.ai-core\u002F04.verification-security-workflow",[4932,4936,4940,4944,4948,4952,4956,4960,4964],{"title":4933,"path":4934,"stem":4935},"Межі можливостей AI","\u002Fai-core\u002Fverification-security-workflow\u002Fai-limitations","19.ai-core\u002F04.verification-security-workflow\u002F01.ai-limitations",{"title":4937,"path":4938,"stem":4939},"Галюцинації AI","\u002Fai-core\u002Fverification-security-workflow\u002Fai-hallucinations","19.ai-core\u002F04.verification-security-workflow\u002F02.ai-hallucinations",{"title":4941,"path":4942,"stem":4943},"Критичне мислення під час роботи з AI","\u002Fai-core\u002Fverification-security-workflow\u002Fcritical-thinking","19.ai-core\u002F04.verification-security-workflow\u002F03.critical-thinking",{"title":4945,"path":4946,"stem":4947},"Перевірка результатів роботи AI","\u002Fai-core\u002Fverification-security-workflow\u002Fresult-verification","19.ai-core\u002F04.verification-security-workflow\u002F04.result-verification",{"title":4949,"path":4950,"stem":4951},"Захист даних під час роботи з AI","\u002Fai-core\u002Fverification-security-workflow\u002Fdata-protection","19.ai-core\u002F04.verification-security-workflow\u002F05.data-protection",{"title":4953,"path":4954,"stem":4955},"Авторське право та навчальна доброчесність","\u002Fai-core\u002Fverification-security-workflow\u002Fcopyright-integrity","19.ai-core\u002F04.verification-security-workflow\u002F06.copyright-integrity",{"title":4957,"path":4958,"stem":4959},"Відповідальне використання AI","\u002Fai-core\u002Fverification-security-workflow\u002Fresponsible-use","19.ai-core\u002F04.verification-security-workflow\u002F07.responsible-use",{"title":4961,"path":4962,"stem":4963},"AI Native Workflow","\u002Fai-core\u002Fverification-security-workflow\u002Fai-native-workflow","19.ai-core\u002F04.verification-security-workflow\u002F08.ai-native-workflow",{"title":4965,"path":4966,"stem":4967},"Застосування AI Native Workflow під час створення цифрового продукту","\u002Fai-core\u002Fverification-security-workflow\u002Fworkflow-in-practice","19.ai-core\u002F04.verification-security-workflow\u002F09.workflow-in-practice",{"title":4969,"icon":4970,"path":4971,"stem":4972,"children":4973,"page":59},"AI Developer: Vibe Coding Sprint","i-lucide-code-xml","\u002Fai-developer","20.ai-developer",[4974,5000,5022,5043],{"title":4975,"icon":4976,"path":4977,"stem":4978,"children":4979,"page":59},"Проєктування і чорнова версія","i-lucide-pencil-ruler","\u002Fai-developer\u002Fdraft-creation","20.ai-developer\u002F01.draft-creation",[4980,4984,4988,4992,4996],{"title":4981,"path":4982,"stem":4983},"Парадигма Vibe Coding Workflow","\u002Fai-developer\u002Fdraft-creation\u002Fvibe-coding-workflow","20.ai-developer\u002F01.draft-creation\u002F01.vibe-coding-workflow",{"title":4985,"path":4986,"stem":4987},"AI-середовище швидкої розробки","\u002Fai-developer\u002Fdraft-creation\u002Fai-ide-environment","20.ai-developer\u002F01.draft-creation\u002F02.ai-ide-environment",{"title":4989,"path":4990,"stem":4991},"Користувацьке завдання та межі першої версії","\u002Fai-developer\u002Fdraft-creation\u002Fproduct-scope-and-mvp","20.ai-developer\u002F01.draft-creation\u002F03.product-scope-and-mvp",{"title":4993,"path":4994,"stem":4995},"Архітектура інтерфейсу та стани застосунку","\u002Fai-developer\u002Fdraft-creation\u002Fapplication-architecture-and-states","20.ai-developer\u002F01.draft-creation\u002F04.application-architecture-and-states",{"title":4997,"path":4998,"stem":4999},"Генерація та фіксація чорнової версії","\u002Fai-developer\u002Fdraft-creation\u002Fgenerating-draft-version","20.ai-developer\u002F01.draft-creation\u002F05.generating-draft-version",{"title":5001,"icon":5002,"path":5003,"stem":5004,"children":5005,"page":59},"Перша робоча версія","i-lucide-play-circle","\u002Fai-developer\u002Ffirst-working-version","20.ai-developer\u002F02.first-working-version",[5006,5010,5014,5018],{"title":5007,"path":5008,"stem":5009},"Уточнення контексту проєкту для AI","\u002Fai-developer\u002Ffirst-working-version\u002Frefining-project-context","20.ai-developer\u002F02.first-working-version\u002F01.refining-project-context",{"title":5011,"path":5012,"stem":5013},"Стратегія завдань для AI-розробки","\u002Fai-developer\u002Ffirst-working-version\u002Fai-prompting-strategy","20.ai-developer\u002F02.first-working-version\u002F02.ai-prompting-strategy",{"title":5015,"path":5016,"stem":5017},"Доопрацювання до першої робочої версії","\u002Fai-developer\u002Ffirst-working-version\u002Frefining-to-working-version","20.ai-developer\u002F02.first-working-version\u002F03.refining-to-working-version",{"title":5019,"path":5020,"stem":5021},"Перевірка першої робочої версії","\u002Fai-developer\u002Ffirst-working-version\u002Ftesting-first-working-version","20.ai-developer\u002F02.first-working-version\u002F04.testing-first-working-version",{"title":5023,"icon":3072,"path":5024,"stem":5025,"children":5026,"page":59},"Ітерації, дебагінг та відновлення","\u002Fai-developer\u002Fiteration-debugging-recovery","20.ai-developer\u002F03.iteration-debugging-recovery",[5027,5031,5035,5039],{"title":5028,"path":5029,"stem":5030},"Управління точковими змінами продукту","\u002Fai-developer\u002Fiteration-debugging-recovery\u002Fchange-management","20.ai-developer\u002F03.iteration-debugging-recovery\u002F01.change-management",{"title":5032,"path":5033,"stem":5034},"Надійність інтерфейсу та обробка помилок","\u002Fai-developer\u002Fiteration-debugging-recovery\u002Fscenario-reliability-and-errors","20.ai-developer\u002F03.iteration-debugging-recovery\u002F02.scenario-reliability-and-errors",{"title":5036,"path":5037,"stem":5038},"Керування контекстом і збереження версій","\u002Fai-developer\u002Fiteration-debugging-recovery\u002Fproject-context-and-versions","20.ai-developer\u002F03.iteration-debugging-recovery\u002F03.project-context-and-versions",{"title":5040,"path":5041,"stem":5042},"Збої AI-розробки, ліміти та аварійне відновлення","\u002Fai-developer\u002Fiteration-debugging-recovery\u002Fai-breakdowns-and-recovery","20.ai-developer\u002F03.iteration-debugging-recovery\u002F04.ai-breakdowns-and-recovery",{"title":5044,"icon":5045,"path":5046,"stem":5047,"children":5048,"page":59},"Перевірка та завершення продукту","i-lucide-check-check","\u002Fai-developer\u002Fverification-and-completion","20.ai-developer\u002F04.verification-and-completion",[5049,5053,5057],{"title":5050,"path":5051,"stem":5052},"Комплексний аудит та підсумкове тестування","\u002Fai-developer\u002Fverification-and-completion\u002Ffinal-product-testing","20.ai-developer\u002F04.verification-and-completion\u002F01.final-product-testing",{"title":5054,"path":5055,"stem":5056},"Підготовка підсумкової версії коду","\u002Fai-developer\u002Fverification-and-completion\u002Fpreparing-release-version","20.ai-developer\u002F04.verification-and-completion\u002F02.preparing-release-version",{"title":5058,"path":5059,"stem":5060},"Демонстрація, публікація та критерії оцінювання","\u002Fai-developer\u002Fverification-and-completion\u002Fproduct-showcase-and-publishing","20.ai-developer\u002F04.verification-and-completion\u002F03.product-showcase-and-publishing",{"title":5062,"path":5063,"stem":5064,"children":5065,"page":59},"Tailwind","\u002Ftailwind","21.tailwind",[5066,5070,5074,5078,5082,5086,5090,5094,5098,5102,5106,5110],{"title":5067,"path":5068,"stem":5069},"Що таке Tailwind CSS і навіщо він потрібен","\u002Ftailwind\u002Ftailwind-intro-philosophy","21.tailwind\u002F01.tailwind-intro-philosophy",{"title":5071,"path":5072,"stem":5073},"Встановлення та налаштування Tailwind CSS v4","\u002Ftailwind\u002Ftailwind-installation-setup","21.tailwind\u002F02.tailwind-installation-setup",{"title":5075,"path":5076,"stem":5077},"Utility-класи: основи та система Tailwind","\u002Ftailwind\u002Ftailwind-utility-classes-core","21.tailwind\u002F03.tailwind-utility-classes-core",{"title":5079,"path":5080,"stem":5081},"Layout: Flexbox та Grid через Tailwind","\u002Ftailwind\u002Ftailwind-flexbox-grid","21.tailwind\u002F04.tailwind-flexbox-grid",{"title":5083,"path":5084,"stem":5085},"Кастомізація теми через @theme у Tailwind v4","\u002Ftailwind\u002Ftailwind-theme-customization","21.tailwind\u002F05.tailwind-theme-customization",{"title":5087,"path":5088,"stem":5089},"Варіанти: hover, focus, responsive, dark mode та нові v4","\u002Ftailwind\u002Ftailwind-variants-states","21.tailwind\u002F06.tailwind-variants-states",{"title":5091,"path":5092,"stem":5093},"Типографіка та система кольорів у Tailwind v4","\u002Ftailwind\u002Ftailwind-typography-colors","21.tailwind\u002F07.tailwind-typography-colors",{"title":5095,"path":5096,"stem":5097},"Компоненти та повторюваність: @apply, @utility та патерни","\u002Ftailwind\u002Ftailwind-components-patterns","21.tailwind\u002F08.tailwind-components-patterns",{"title":5099,"path":5100,"stem":5101},"Темна тема та система дизайн-токенів у Tailwind v4","\u002Ftailwind\u002Ftailwind-dark-mode-theming","21.tailwind\u002F09.tailwind-dark-mode-theming",{"title":5103,"path":5104,"stem":5105},"Довільні значення та контейнерні запити у Tailwind v4","\u002Ftailwind\u002Ftailwind-arbitrary-container-queries","21.tailwind\u002F10.tailwind-arbitrary-container-queries",{"title":5107,"path":5108,"stem":5109},"Анімації, трансформації та 3D у Tailwind v4","\u002Ftailwind\u002Ftailwind-animations-transforms","21.tailwind\u002F11.tailwind-animations-transforms",{"title":5111,"path":5112,"stem":5113},"Tailwind CLI, PostCSS та інтеграція з фреймворками","\u002Ftailwind\u002Ftailwind-cli-tooling","21.tailwind\u002F12.tailwind-cli-tooling",{"title":5115,"path":5116,"stem":5117,"children":5118,"page":59},"React Typescript","\u002Freact-typescript","22.react-typescript",[5119,5144,5148,5152,5156,5160,5164,5168,5172],{"title":5120,"path":5121,"stem":5122,"children":5123,"page":59},"From Html To Project","\u002Freact-typescript\u002Ffrom-html-to-project","22.react-typescript\u002F01.from-html-to-project",[5124,5128,5132,5136,5140],{"title":5125,"path":5126,"stem":5127},"Від одного HTML-файлу до повноцінного проєкту","\u002Freact-typescript\u002Ffrom-html-to-project\u002Fevolution","22.react-typescript\u002F01.from-html-to-project\u002F01.evolution",{"title":5129,"path":5130,"stem":5131},"Що таке React","\u002Freact-typescript\u002Ffrom-html-to-project\u002Fwhat-is-react","22.react-typescript\u002F01.from-html-to-project\u002F02.what-is-react",{"title":5133,"path":5134,"stem":5135},"JSX та TSX — синтаксичний цукор, а не магія","\u002Freact-typescript\u002Ffrom-html-to-project\u002Fjsx-tsx-syntax","22.react-typescript\u002F01.from-html-to-project\u002F03.jsx-tsx-syntax",{"title":5137,"path":5138,"stem":5139},"Компоненти — будівельні блоки інтерфейсу","\u002Freact-typescript\u002Ffrom-html-to-project\u002Fcomponents","22.react-typescript\u002F01.from-html-to-project\u002F04.components",{"title":5141,"path":5142,"stem":5143},"JSX — розмітка всередині TypeScript","\u002Freact-typescript\u002Ffrom-html-to-project\u002Fjsx-expressions","22.react-typescript\u002F01.from-html-to-project\u002F05.jsx-expressions",{"title":5145,"path":5146,"stem":5147},"Хук useReducer: структуроване управління складним станом","\u002Freact-typescript\u002Fuse-reducer","22.react-typescript\u002F23.use-reducer",{"title":5149,"path":5150,"stem":5151},"Context API: передавання даних без «пропс-дрілінгу»","\u002Freact-typescript\u002Fcontext-api","22.react-typescript\u002F24.context-api",{"title":5153,"path":5154,"stem":5155},"Синхронізація з ефектами: хук useEffect та функції очищення","\u002Freact-typescript\u002Fuse-effect","22.react-typescript\u002F27.use-effect",{"title":5157,"path":5158,"stem":5159},"Можливо, вам не потрібен ефект: антипатерни та альтернативи","\u002Freact-typescript\u002Fyou-might-not-need-effect","22.react-typescript\u002F28.you-might-not-need-effect",{"title":5161,"path":5162,"stem":5163},"Життєвий цикл реактивних ефектів: мислення процесами синхронізації","\u002Freact-typescript\u002Flifecycle-of-reactive-effects","22.react-typescript\u002F29.lifecycle-of-reactive-effects",{"title":5165,"path":5166,"stem":5167},"Розділення подій та ефектів: реактивна проти нереактивної логіки","\u002Freact-typescript\u002Fseparating-events-from-effects","22.react-typescript\u002F30.separating-events-from-effects",{"title":5169,"path":5170,"stem":5171},"Видалення зайвих залежностей ефектів та оптимізація синхронізації","\u002Freact-typescript\u002Fremoving-effect-dependencies","22.react-typescript\u002F31.removing-effect-dependencies",{"title":5173,"path":5174,"stem":5175},"Кастомні хуки: повторне використання та композиція реактивної логіки","\u002Freact-typescript\u002Fcustom-hooks","22.react-typescript\u002F32.custom-hooks",{"title":5177,"path":5178,"stem":5179},"Тестування компонентів діаграм та пісочниць","\u002Ftest-components","98.test-components",{"id":5181,"title":4045,"body":5182,"description":24297,"extension":14555,"links":24298,"meta":24299,"navigation":5528,"path":4046,"seo":24300,"stem":4047,"__hash__":24301},"docs\u002F17.client-server-architecture\u002F15.grpc-inter-service-communication.md",{"type":5183,"value":5184,"toc":24242},"minimark",[5185,5189,5266,5269,5274,5279,5292,5295,5300,5303,5310,5315,5330,5341,5346,5356,5375,5389,5394,5404,5415,5429,5433,5437,5447,5450,5455,5853,5860,5865,5978,5988,5992,5995,6000,6003,6008,6011,6016,6019,6024,6027,6032,6035,6040,6043,6075,6378,6380,6384,6388,6402,6424,6447,6451,6469,6685,6690,6698,6715,6723,6729,6737,6740,6748,6751,6803,6811,6826,6831,6838,6847,6851,6854,7042,7065,7069,7079,7272,7277,7287,7292,7310,7341,7345,7358,7635,7640,7852,7857,7865,7870,7916,7921,7965,7970,8050,8057,8066,8075,8089,8091,8095,8099,8102,8107,8118,8121,8126,8129,8209,8212,8217,8220,8223,8227,8238,8243,8248,8265,8268,8273,8276,8282,8313,8318,8325,8336,8341,8352,8357,8360,8561,8565,8568,8573,8576,8581,8591,8602,8607,8610,8615,8618,8625,8627,8631,8635,8645,8652,8970,8977,8987,9030,9036,9042,9048,9054,9074,9078,9081,9564,9569,9572,9578,9583,9586,9590,9604,9609,9612,9616,9630,9635,9638,9642,9656,9763,9765,9769,9773,9781,9786,10031,10036,10050,10063,10070,10609,10614,10624,10634,10648,10657,10665,11119,11124,11145,11159,11169,11178,11192,11197,11588,11592,11598,11616,11629,11638,11640,11644,11648,11655,12582,12587,12604,12620,12632,12641,12651,12670,12685,12703,12716,12720,12727,12732,13102,13107,13617,13638,13640,13644,13648,13657,13677,13683,13697,13701,13704,14143,14147,14156,14172,14191,14208,14214,14223,14227,14230,14956,14961,14974,14988,15002,15016,15025,16523,16527,16533,16862,16878,16909,16921,17630,17632,17636,17640,17643,18226,18230,18233,18998,19007,19017,19021,19031,19064,19069,19218,19237,19247,19251,19685,19687,19691,19695,19897,19901,19906,19941,19946,19982,19989,19991,19995,19999,20005,20010,20015,20022,20028,20033,20036,20163,20170,20174,20184,20455,20460,20482,20486,20497,20502,20865,20870,21184,21192,21196,21777,21782,21805,21810,21830,21833,21910,22036,22038,22040,22044,23805,23807,23811,23894,24127,24129,24133,24238],[5186,5187,4045],"h1",{"id":5188},"міжсервісна-взаємодія-на-основі-протоколу-grpc",[5190,5191,5192,5224],"card-group",{},[5193,5194,5197],"card",{"icon":5195,"title":5196},"i-lucide-target","🎯 Мета лекції",[5198,5199,5200,5204,5207,5215,5218,5221],"ul",{},[5201,5202,5203],"li",{},"Зрозуміти фундаментальні обмеження REST API та причини виникнення RPC-підходів у розподілених системах.",[5201,5205,5206],{},"Опанувати концепцію віддаленого виклику процедур (Remote Procedure Call) та її еволюцію від XML-RPC до gRPC.",[5201,5208,5209,5210,5214],{},"Вивчити формат бінарної серіалізації Protocol Buffers (Protobuf) та синтаксис файлів ",[5211,5212,5213],"code",{},".proto",".",[5201,5216,5217],{},"Зрозуміти архітектуру транспортного рівня gRPC на базі HTTP\u002F2 та його переваги над HTTP\u002F1.1.",[5201,5219,5220],{},"Навчитися проєктувати та реалізовувати gRPC-сервіси у Go з підтримкою різних типів викликів.",[5201,5222,5223],{},"Застосовувати інтерцептори для автентифікації, логування та обробки помилок у gRPC.",[5193,5225,5227],{"icon":4618,"title":5226},"🔑 Ключові терміни",[5198,5228,5229,5236,5242,5248,5254,5260],{},[5201,5230,5231,5235],{},[5232,5233,5234],"strong",{},"RPC (Remote Procedure Call):"," парадигма міжпроцесної комунікації, що дозволяє викликати функції на віддаленій машині так, ніби вони є локальними.",[5201,5237,5238,5241],{},[5232,5239,5240],{},"Protocol Buffers (Protobuf):"," мова опису структур даних та формат бінарної серіалізації, розроблений компанією Google.",[5201,5243,5244,5247],{},[5232,5245,5246],{},"gRPC:"," високопродуктивний RPC-фреймворк із відкритим вихідним кодом, що використовує HTTP\u002F2 як транспорт та Protobuf як формат серіалізації.",[5201,5249,5250,5253],{},[5232,5251,5252],{},"Stub:"," згенерований клієнтський код, що інкапсулює деталі мережевої комунікації та надає локальний інтерфейс для віддалених викликів.",[5201,5255,5256,5259],{},[5232,5257,5258],{},"Streaming RPC:"," тип виклику, де клієнт або сервер (або обидві сторони) надсилають послідовність повідомлень у рамках одного запиту.",[5201,5261,5262,5265],{},[5232,5263,5264],{},"Interceptor:"," middleware-компонент gRPC, що перехоплює виклики для додавання наскрізної функціональності (логування, метрики, автентифікація).",[5267,5268],"hr",{},[5270,5271,5273],"h2",{"id":5272},"еволюція-міжсервісної-комунікації-від-rest-до-rpc","Еволюція міжсервісної комунікації: від REST до RPC",[5275,5276,5278],"h3",{"id":5277},"архітектурні-обмеження-rest-api-у-розподілених-системах","Архітектурні обмеження REST API у розподілених системах",[5280,5281,5282,5283,5287,5288,5291],"p",{},"REST (Representational State Transfer) є домінуючим архітектурним стилем для побудови вебсервісів протягом останніх двох десятиліть. REST-архітектура базується на кількох фундаментальних принципах: ресурсна орієнтація, використання стандартних HTTP-методів (GET, POST, PUT, DELETE), відсутність стану на сервері (",[5284,5285,5286],"em",{},"stateless",") та єдиний інтерфейс (",[5284,5289,5290],{},"uniform interface","). Ці принципи забезпечили REST величезну популярність завдяки простоті розуміння, легкості інтеграції та універсальності.",[5280,5293,5294],{},"Проте при побудові великих розподілених систем, що складаються з десятків або сотень мікросервісів, REST-підхід виявляє кілька суттєвих обмежень, які стають критичними для продуктивності та зручності розробки.",[5280,5296,5297],{},[5232,5298,5299],{},"Обмеження 1: Текстовий формат серіалізації та накладні витрати JSON.",[5280,5301,5302],{},"Майже всі REST API використовують JSON як формат обміну даними. JSON є текстовим форматом, що робить його легкочитаним для людини та простим у налагодженні. Однак серіалізація та десеріалізація JSON є обчислювально витратною операцією. Парсинг JSON вимагає лексичного аналізу, побудови дерева об'єктів у пам'яті та приведення типів. Для високонавантажених систем, де окремий сервіс обробляє десятки тисяч запитів на секунду, ці витрати можуть становити 15–25% процесорного часу.",[5280,5304,5305,5306,5309],{},"Крім того, JSON не має вбудованої схеми (",[5284,5307,5308],{},"schema","). Структура даних визначається лише документацією (OpenAPI\u002FSwagger) або прикладами, що призводить до проблем сумісності версій. Якщо сервер додає нове поле або змінює тип існуючого поля, старі клієнти можуть некоректно інтерпретувати відповідь або взагалі відхилити її.",[5280,5311,5312],{},[5232,5313,5314],{},"Обмеження 2: Відсутність строгих контрактів між клієнтом та сервером.",[5280,5316,5317,5318,5321,5322,5325,5326,5329],{},"REST API зазвичай документуються за допомогою специфікацій OpenAPI (Swagger), які описують ендпоінти, параметри, типи відповідей та коди помилок. Проте ці специфікації є ",[5232,5319,5320],{},"описовими, а не обов'язковими до виконання"," (",[5284,5323,5324],{},"descriptive, not prescriptive","). Немає гарантії, що сервер дійсно відповідає специфікації, або що клієнт коректно обробляє всі можливі відповіді. Помилки невідповідності виявляються лише під час виконання (",[5284,5327,5328],{},"runtime","), часто вже у продакшн-середовищі.",[5280,5331,5332,5333,5336,5337,5340],{},"Кодогенерація клієнтських бібліотек зі специфікації OpenAPI існує (наприклад, інструменти ",[5211,5334,5335],{},"openapi-generator",", ",[5211,5338,5339],{},"swagger-codegen","), але згенерований код часто є громіздким, важким у підтримці та не завжди коректно відображає реальну поведінку API. Розробники змушені вручну перевіряти відповідність коду специфікації або писати інтеграційні тести для кожного ендпоінту.",[5280,5342,5343],{},[5232,5344,5345],{},"Обмеження 3: Неефективність HTTP\u002F1.1 для міжсервісної комунікації.",[5280,5347,5348,5349,5321,5352,5355],{},"Більшість REST API працюють поверх протоколу HTTP\u002F1.1, який має кілька вбудованих обмежень. По-перше, HTTP\u002F1.1 не підтримує ",[5232,5350,5351],{},"мультиплексування запитів",[5284,5353,5354],{},"request multiplexing","). Якщо клієнт хоче виконати кілька паралельних запитів до одного сервера, він повинен відкрити кілька TCP-з'єднань (зазвичай браузери обмежують це значення до 6 з'єднань на домен). Кожне нове з'єднання вимагає виконання TCP-рукостискання (3-Way Handshake), що додає 1–2 додаткові мережеві обміни та затримку у 50–200 мілісекунд залежно від географічної відстані.",[5280,5357,5358,5359,5336,5362,5336,5365,5336,5368,5336,5371,5374],{},"По-друге, HTTP\u002F1.1 передає заголовки у текстовому форматі без стиснення. Типовий HTTP-запит містить сотні байтів заголовків (",[5211,5360,5361],{},"Host",[5211,5363,5364],{},"User-Agent",[5211,5366,5367],{},"Accept",[5211,5369,5370],{},"Authorization",[5211,5372,5373],{},"Content-Type"," тощо), які повторюються у кожному запиті. Для коротких запитів (наприклад, отримання статусу завдання) розмір заголовків може перевищувати розмір корисного навантаження у 3–5 разів.",[5280,5376,5377,5378,5321,5381,5384,5385,5388],{},"По-третє, HTTP\u002F1.1 є ",[5232,5379,5380],{},"напівдуплексним протоколом",[5284,5382,5383],{},"half-duplex","): клієнт надсилає запит, чекає на відповідь, і лише після цього може надіслати наступний запит у тому ж з'єднанні. Це призводить до ефекту ",[5232,5386,5387],{},"Head-of-Line Blocking",", коли повільний запит блокує всі наступні запити у черзі.",[5280,5390,5391],{},[5232,5392,5393],{},"Обмеження 4: Відсутність нативної підтримки потокових операцій.",[5280,5395,5396,5397,5321,5400,5403],{},"REST є моделлю «запит-відповідь», де кожен запит очікує єдину завершену відповідь. Проте багато реальних сценаріїв вимагають ",[5232,5398,5399],{},"потокової передачі даних",[5284,5401,5402],{},"streaming","). Наприклад:",[5198,5405,5406,5409,5412],{},[5201,5407,5408],{},"Завантаження великого списку об'єктів (мільйони записів) частинами, не завантажуючи весь набір у пам'ять.",[5201,5410,5411],{},"Відстеження прогресу довготривалої операції (експорт даних, обробка відео).",[5201,5413,5414],{},"Отримання оновлень у реальному часі (фінансові котирування, телеметрія IoT-пристроїв).",[5280,5416,5417,5418,5336,5421,5424,5425,5428],{},"Для реалізації цих сценаріїв у REST доводиться використовувати обхідні шляхи: пагінацію (клієнт виконує серію запитів ",[5211,5419,5420],{},"GET \u002Fitems?page=1",[5211,5422,5423],{},"GET \u002Fitems?page=2","...), довге опитування (",[5284,5426,5427],{},"long polling",") або відкривати окремі WebSocket-з'єднання поза REST API. Усі ці підходи додають архітектурну складність та зменшують ефективність системи.",[5430,5431,5432],"note",{},"REST залишається оптимальним вибором для публічних API, що призначені для споживання вебклієнтами, мобільними застосунками або сторонніми інтеграторами. Простота, універсальність та підтримка інструментарію роблять REST ідеальним для зовнішніх інтерфейсів. Проте для внутрішньої комунікації між мікросервісами, де критичні продуктивність, строгість контрактів та ефективність мережевого обміну, RPC-підходи демонструють значні переваги.",[5275,5434,5436],{"id":5435},"концепція-віддаленого-виклику-процедур-rpc","Концепція віддаленого виклику процедур (RPC)",[5280,5438,5439,5442,5443,5446],{},[5232,5440,5441],{},"Remote Procedure Call (RPC)"," — це парадигма комп'ютерної комунікації, що дозволяє програмі викликати функцію або процедуру, яка виконується на іншій машині (або в іншому процесі), так, ніби це локальний виклик. Основна ідея RPC полягає у тому, щоб ",[5232,5444,5445],{},"абстрагувати деталі мережевої взаємодії"," та надати розробнику зручний інтерфейс, що не відрізняється від звичайних викликів функцій.",[5280,5448,5449],{},"Розглянемо різницю між REST та RPC на прикладі простої операції створення користувача.",[5280,5451,5452],{},[5232,5453,5454],{},"REST-підхід:",[5456,5457,5462],"pre",{"className":5458,"code":5459,"language":5460,"meta":5461,"style":5461},"language-go shiki shiki-themes light-plus dark-plus dark-plus","\u002F\u002F Клієнтський код\ntype User struct {\n    Name  string `json:\"name\"`\n    Email string `json:\"email\"`\n}\n\nfunc createUserREST(apiURL string, user User) error {\n    payload, _ := json.Marshal(user)\n    \n    resp, err := http.Post(\n        apiURL+\"\u002Fapi\u002Fv1\u002Fusers\",\n        \"application\u002Fjson\",\n        bytes.NewReader(payload),\n    )\n    if err != nil {\n        return err\n    }\n    defer resp.Body.Close()\n    \n    if resp.StatusCode != http.StatusCreated {\n        body, _ := io.ReadAll(resp.Body)\n        return fmt.Errorf(\"API error %d: %s\", resp.StatusCode, body)\n    }\n    \n    return nil\n}\n","go","",[5211,5463,5464,5473,5491,5505,5517,5523,5530,5563,5592,5598,5622,5637,5645,5664,5670,5688,5697,5703,5725,5730,5753,5784,5829,5834,5839,5848],{"__ignoreMap":5461},[5465,5466,5469],"span",{"class":5467,"line":5468},"line",1,[5465,5470,5472],{"class":5471},"spJ8K","\u002F\u002F Клієнтський код\n",[5465,5474,5476,5480,5484,5487],{"class":5467,"line":5475},2,[5465,5477,5479],{"class":5478},"su1O8","type",[5465,5481,5483],{"class":5482},"sN1BT"," User",[5465,5485,5486],{"class":5478}," struct",[5465,5488,5490],{"class":5489},"sHH4Y"," {\n",[5465,5492,5494,5498,5501],{"class":5467,"line":5493},3,[5465,5495,5497],{"class":5496},"siwwj","    Name",[5465,5499,5500],{"class":5482},"  string",[5465,5502,5504],{"class":5503},"sbdoH"," `json:\"name\"`\n",[5465,5506,5508,5511,5514],{"class":5467,"line":5507},4,[5465,5509,5510],{"class":5496},"    Email",[5465,5512,5513],{"class":5482}," string",[5465,5515,5516],{"class":5503}," `json:\"email\"`\n",[5465,5518,5520],{"class":5467,"line":5519},5,[5465,5521,5522],{"class":5489},"}\n",[5465,5524,5526],{"class":5467,"line":5525},6,[5465,5527,5529],{"emptyLinePlaceholder":5528},true,"\n",[5465,5531,5533,5536,5540,5543,5546,5548,5550,5553,5555,5558,5561],{"class":5467,"line":5532},7,[5465,5534,5535],{"class":5478},"func",[5465,5537,5539],{"class":5538},"s8Opu"," createUserREST",[5465,5541,5542],{"class":5489},"(",[5465,5544,5545],{"class":5496},"apiURL",[5465,5547,5513],{"class":5482},[5465,5549,5336],{"class":5489},[5465,5551,5552],{"class":5496},"user",[5465,5554,5483],{"class":5482},[5465,5556,5557],{"class":5489},") ",[5465,5559,5560],{"class":5482},"error",[5465,5562,5490],{"class":5489},[5465,5564,5566,5569,5571,5574,5577,5580,5582,5585,5587,5589],{"class":5467,"line":5565},8,[5465,5567,5568],{"class":5496},"    payload",[5465,5570,5336],{"class":5489},[5465,5572,5573],{"class":5496},"_",[5465,5575,5576],{"class":5489}," := ",[5465,5578,5579],{"class":5496},"json",[5465,5581,5214],{"class":5489},[5465,5583,5584],{"class":5538},"Marshal",[5465,5586,5542],{"class":5489},[5465,5588,5552],{"class":5496},[5465,5590,5591],{"class":5489},")\n",[5465,5593,5595],{"class":5467,"line":5594},9,[5465,5596,5597],{"class":5489},"    \n",[5465,5599,5601,5604,5606,5609,5611,5614,5616,5619],{"class":5467,"line":5600},10,[5465,5602,5603],{"class":5496},"    resp",[5465,5605,5336],{"class":5489},[5465,5607,5608],{"class":5496},"err",[5465,5610,5576],{"class":5489},[5465,5612,5613],{"class":5496},"http",[5465,5615,5214],{"class":5489},[5465,5617,5618],{"class":5538},"Post",[5465,5620,5621],{"class":5489},"(\n",[5465,5623,5625,5628,5631,5634],{"class":5467,"line":5624},11,[5465,5626,5627],{"class":5496},"        apiURL",[5465,5629,5630],{"class":5489},"+",[5465,5632,5633],{"class":5503},"\"\u002Fapi\u002Fv1\u002Fusers\"",[5465,5635,5636],{"class":5489},",\n",[5465,5638,5640,5643],{"class":5467,"line":5639},12,[5465,5641,5642],{"class":5503},"        \"application\u002Fjson\"",[5465,5644,5636],{"class":5489},[5465,5646,5648,5651,5653,5656,5658,5661],{"class":5467,"line":5647},13,[5465,5649,5650],{"class":5496},"        bytes",[5465,5652,5214],{"class":5489},[5465,5654,5655],{"class":5538},"NewReader",[5465,5657,5542],{"class":5489},[5465,5659,5660],{"class":5496},"payload",[5465,5662,5663],{"class":5489},"),\n",[5465,5665,5667],{"class":5467,"line":5666},14,[5465,5668,5669],{"class":5489},"    )\n",[5465,5671,5673,5677,5680,5683,5686],{"class":5467,"line":5672},15,[5465,5674,5676],{"class":5675},"s8xlr","    if",[5465,5678,5679],{"class":5496}," err",[5465,5681,5682],{"class":5489}," != ",[5465,5684,5685],{"class":5478},"nil",[5465,5687,5490],{"class":5489},[5465,5689,5691,5694],{"class":5467,"line":5690},16,[5465,5692,5693],{"class":5675},"        return",[5465,5695,5696],{"class":5496}," err\n",[5465,5698,5700],{"class":5467,"line":5699},17,[5465,5701,5702],{"class":5489},"    }\n",[5465,5704,5706,5709,5712,5714,5717,5719,5722],{"class":5467,"line":5705},18,[5465,5707,5708],{"class":5675},"    defer",[5465,5710,5711],{"class":5496}," resp",[5465,5713,5214],{"class":5489},[5465,5715,5716],{"class":5496},"Body",[5465,5718,5214],{"class":5489},[5465,5720,5721],{"class":5538},"Close",[5465,5723,5724],{"class":5489},"()\n",[5465,5726,5728],{"class":5467,"line":5727},19,[5465,5729,5597],{"class":5489},[5465,5731,5733,5735,5737,5739,5742,5744,5746,5748,5751],{"class":5467,"line":5732},20,[5465,5734,5676],{"class":5675},[5465,5736,5711],{"class":5496},[5465,5738,5214],{"class":5489},[5465,5740,5741],{"class":5496},"StatusCode",[5465,5743,5682],{"class":5489},[5465,5745,5613],{"class":5496},[5465,5747,5214],{"class":5489},[5465,5749,5750],{"class":5496},"StatusCreated",[5465,5752,5490],{"class":5489},[5465,5754,5756,5759,5761,5763,5765,5768,5770,5773,5775,5778,5780,5782],{"class":5467,"line":5755},21,[5465,5757,5758],{"class":5496},"        body",[5465,5760,5336],{"class":5489},[5465,5762,5573],{"class":5496},[5465,5764,5576],{"class":5489},[5465,5766,5767],{"class":5496},"io",[5465,5769,5214],{"class":5489},[5465,5771,5772],{"class":5538},"ReadAll",[5465,5774,5542],{"class":5489},[5465,5776,5777],{"class":5496},"resp",[5465,5779,5214],{"class":5489},[5465,5781,5716],{"class":5496},[5465,5783,5591],{"class":5489},[5465,5785,5787,5789,5792,5794,5797,5799,5802,5805,5808,5811,5814,5816,5818,5820,5822,5824,5827],{"class":5467,"line":5786},22,[5465,5788,5693],{"class":5675},[5465,5790,5791],{"class":5496}," fmt",[5465,5793,5214],{"class":5489},[5465,5795,5796],{"class":5538},"Errorf",[5465,5798,5542],{"class":5489},[5465,5800,5801],{"class":5503},"\"API error ",[5465,5803,5804],{"class":5496},"%d",[5465,5806,5807],{"class":5503},": ",[5465,5809,5810],{"class":5496},"%s",[5465,5812,5813],{"class":5503},"\"",[5465,5815,5336],{"class":5489},[5465,5817,5777],{"class":5496},[5465,5819,5214],{"class":5489},[5465,5821,5741],{"class":5496},[5465,5823,5336],{"class":5489},[5465,5825,5826],{"class":5496},"body",[5465,5828,5591],{"class":5489},[5465,5830,5832],{"class":5467,"line":5831},23,[5465,5833,5702],{"class":5489},[5465,5835,5837],{"class":5467,"line":5836},24,[5465,5838,5597],{"class":5489},[5465,5840,5842,5845],{"class":5467,"line":5841},25,[5465,5843,5844],{"class":5675},"    return",[5465,5846,5847],{"class":5478}," nil\n",[5465,5849,5851],{"class":5467,"line":5850},26,[5465,5852,5522],{"class":5489},[5280,5854,5855,5856,5859],{},"Клієнтський код повинен вручну серіалізувати об'єкт у JSON, сконструювати HTTP-запит, перевірити статус-код відповіді, обробити помилки та десеріалізувати тіло відповіді. Усі ці деталі є ",[5232,5857,5858],{},"деталями реалізації транспортного рівня",", які не мають відношення до бізнес-логіки.",[5280,5861,5862],{},[5232,5863,5864],{},"RPC-підхід (концептуально):",[5456,5866,5868],{"className":5458,"code":5867,"language":5460,"meta":5461,"style":5461},"\u002F\u002F Клієнтський код\nclient := userservice.NewClient(\"user-service.local:50051\")\n\nerr := client.CreateUser(ctx, &User{\n    Name:  \"Alice Johnson\",\n    Email: \"alice@example.com\",\n})\nif err != nil {\n    return err\n}\n",[5211,5869,5870,5874,5896,5900,5927,5939,5950,5955,5968,5974],{"__ignoreMap":5461},[5465,5871,5872],{"class":5467,"line":5468},[5465,5873,5472],{"class":5471},[5465,5875,5876,5879,5881,5884,5886,5889,5891,5894],{"class":5467,"line":5475},[5465,5877,5878],{"class":5496},"client",[5465,5880,5576],{"class":5489},[5465,5882,5883],{"class":5496},"userservice",[5465,5885,5214],{"class":5489},[5465,5887,5888],{"class":5538},"NewClient",[5465,5890,5542],{"class":5489},[5465,5892,5893],{"class":5503},"\"user-service.local:50051\"",[5465,5895,5591],{"class":5489},[5465,5897,5898],{"class":5467,"line":5493},[5465,5899,5529],{"emptyLinePlaceholder":5528},[5465,5901,5902,5904,5906,5908,5910,5913,5915,5918,5921,5924],{"class":5467,"line":5507},[5465,5903,5608],{"class":5496},[5465,5905,5576],{"class":5489},[5465,5907,5878],{"class":5496},[5465,5909,5214],{"class":5489},[5465,5911,5912],{"class":5538},"CreateUser",[5465,5914,5542],{"class":5489},[5465,5916,5917],{"class":5496},"ctx",[5465,5919,5920],{"class":5489},", &",[5465,5922,5923],{"class":5482},"User",[5465,5925,5926],{"class":5489},"{\n",[5465,5928,5929,5931,5934,5937],{"class":5467,"line":5519},[5465,5930,5497],{"class":5496},[5465,5932,5933],{"class":5489},":  ",[5465,5935,5936],{"class":5503},"\"Alice Johnson\"",[5465,5938,5636],{"class":5489},[5465,5940,5941,5943,5945,5948],{"class":5467,"line":5525},[5465,5942,5510],{"class":5496},[5465,5944,5807],{"class":5489},[5465,5946,5947],{"class":5503},"\"alice@example.com\"",[5465,5949,5636],{"class":5489},[5465,5951,5952],{"class":5467,"line":5532},[5465,5953,5954],{"class":5489},"})\n",[5465,5956,5957,5960,5962,5964,5966],{"class":5467,"line":5565},[5465,5958,5959],{"class":5675},"if",[5465,5961,5679],{"class":5496},[5465,5963,5682],{"class":5489},[5465,5965,5685],{"class":5478},[5465,5967,5490],{"class":5489},[5465,5969,5970,5972],{"class":5467,"line":5594},[5465,5971,5844],{"class":5675},[5465,5973,5696],{"class":5496},[5465,5975,5976],{"class":5467,"line":5600},[5465,5977,5522],{"class":5489},[5280,5979,5980,5981,5983,5984,5987],{},"Клієнт викликає метод ",[5211,5982,5912],{}," як звичайну функцію. Усі деталі серіалізації, мережевої передачі, обробки помилок та десеріалізації приховані всередині згенерованого клієнтського коду (",[5284,5985,5986],{},"stub","). З точки зору розробника, виклик виглядає локальним.",[5275,5989,5991],{"id":5990},"історична-еволюція-rpc-технологій","Історична еволюція RPC-технологій",[5280,5993,5994],{},"Концепція RPC не є новою — її витоки сягають 1970-х років. Розглянемо ключові віхи еволюції RPC-систем, щоб зрозуміти, які проблеми вирішує сучасний gRPC.",[5280,5996,5997],{},[5232,5998,5999],{},"1. Sun RPC \u002F ONC RPC (1980-ті роки).",[5280,6001,6002],{},"Один із перших стандартизованих RPC-фреймворків, розроблений компанією Sun Microsystems. Використовувався у розподілених файлових системах (NFS — Network File System). Контракти описувалися мовою XDR (External Data Representation), що визначала формат серіалізації. Sun RPC був обмежений мовою C та платформою Unix, що унеможливлювало міжплатформну інтеграцію.",[5280,6004,6005],{},[5232,6006,6007],{},"2. CORBA (Common Object Request Broker Architecture, 1990-ті роки).",[5280,6009,6010],{},"Амбітний стандарт, розроблений консорціумом OMG (Object Management Group), що мав на меті забезпечити RPC-комунікацію між об'єктами, написаними різними мовами програмування (C++, Java, Ada). Контракти описувалися мовою IDL (Interface Definition Language). CORBA був надзвичайно складним у налаштуванні та підтримці, що призвело до його поступового відмирання на початку 2000-х років.",[5280,6012,6013],{},[5232,6014,6015],{},"3. XML-RPC та SOAP (2000-ті роки).",[5280,6017,6018],{},"XML-RPC запропонував простий текстовий формат виклику процедур через HTTP, використовуючи XML для серіалізації параметрів та результатів. SOAP (Simple Object Access Protocol) розширив XML-RPC, додавши підтримку складних типів даних, безпеки (WS-Security), транзакцій (WS-Transaction) та надійної доставки. SOAP став стандартом у корпоративному світі (банки, телекомунікації, державні системи), але його складність та багатослівність XML призвели до появи альтернатив.",[5280,6020,6021],{},[5232,6022,6023],{},"4. JSON-RPC (середина 2000-х років).",[5280,6025,6026],{},"Спрощена версія XML-RPC, що використовує JSON замість XML. JSON-RPC набув популярності у веброзробці завдяки легкості інтеграції з JavaScript, але не отримав широкого визнання у сфері міжсервісної комунікації через відсутність стандартизації та промислових реалізацій.",[5280,6028,6029],{},[5232,6030,6031],{},"5. Apache Thrift та Avro (2007–2009).",[5280,6033,6034],{},"Проєкти з відкритим вихідним кодом, розроблені компаніями Facebook (Thrift) та LinkedIn (Avro у складі Hadoop). Обидва фреймворки використовують бінарну серіалізацію та генерацію коду для різних мов програмування. Thrift набув популярності у розподілених системах, але його складність та фрагментація реалізацій обмежували прийняття.",[5280,6036,6037],{},[5232,6038,6039],{},"6. gRPC (2015, Google).",[5280,6041,6042],{},"Сучасний RPC-фреймворк, розроблений компанією Google на основі внутрішньої системи Stubby, що обслуговує мільярди запитів на секунду у інфраструктурі Google. gRPC поєднує кращі риси попередніх технологій:",[5198,6044,6045,6051,6057,6063,6069],{},[5201,6046,6047,6050],{},[5232,6048,6049],{},"Бінарну серіалізацію Protocol Buffers"," для максимальної продуктивності.",[5201,6052,6053,6056],{},[5232,6054,6055],{},"HTTP\u002F2 як транспортний протокол"," для мультиплексування, стиснення заголовків та двостороннього потокового обміну.",[5201,6058,6059,6062],{},[5232,6060,6061],{},"Кодогенерацію для десятків мов програмування"," (Go, Java, C++, Python, C#, JavaScript, Rust тощо).",[5201,6064,6065,6068],{},[5232,6066,6067],{},"Вбудовану підтримку потокових операцій"," (Server Streaming, Client Streaming, Bidirectional Streaming).",[5201,6070,6071,6074],{},[5232,6072,6073],{},"Розширену екосистему інструментів"," для балансування навантаження, моніторингу, трейсингу та service mesh інтеграції (Istio, Linkerd).",[6076,6077,6079],"plant-uml",{"alt":6078},"Еволюція RPC-технологій та їх характеристики",[5456,6080,6084],{"className":6081,"code":6082,"language":6083,"meta":5461,"style":5461},"language-plantuml shiki shiki-themes light-plus dark-plus dark-plus","@startuml\nskinparam style plain\nskinparam backgroundColor #FFFFFF\nskinparam timeline {\n    BackgroundColor #E2E8F0\n}\n\nscale 1.2\n\n|Епоха мейнфреймів|\n:1970–1980\nSun RPC \u002F ONC RPC;\nnote right #FEF3C7\n  - Мова C, Unix\n  - XDR серіалізація\n  - NFS, RPC Compiler\nend note\n\n|Об'єктно-орієнтована ера|\n:1990–2000\nCORBA \u002F DCOM;\nnote right #FECACA\n  - Надзвичайна складність\n  - IDL, ORB, Name Service\n  - Міжмовна інтеграція\nend note\n\n|Епоха веб-сервісів|\n:2000–2010\nXML-RPC → SOAP;\nnote right #DBEAFE\n  - XML серіалізація\n  - WS-* стандарти\n  - Корпоративні системи\nend note\n\n:2007–2009\nApache Thrift, Avro;\nnote right #DCFCE7\n  - Бінарна серіалізація\n  - Facebook, LinkedIn\n  - Hadoop екосистема\nend note\n\n|Епоха мікросервісів|\n:2015–теперішній час\n**gRPC + Protobuf**;\nnote right #D4F4DD\n  - HTTP\u002F2 транспорт\n  - Потокові виклики\n  - Service Mesh ready\n  - Кросплатформенність\nend note\n\n@enduml\n","plantuml",[5211,6085,6086,6091,6096,6101,6106,6111,6115,6119,6124,6128,6133,6138,6143,6148,6153,6158,6163,6168,6172,6177,6182,6187,6192,6197,6202,6207,6211,6216,6222,6228,6234,6240,6246,6252,6258,6263,6268,6274,6280,6286,6292,6298,6304,6309,6314,6320,6326,6332,6338,6344,6350,6356,6362,6367,6372],{"__ignoreMap":5461},[5465,6087,6088],{"class":5467,"line":5468},[5465,6089,6090],{},"@startuml\n",[5465,6092,6093],{"class":5467,"line":5475},[5465,6094,6095],{},"skinparam style plain\n",[5465,6097,6098],{"class":5467,"line":5493},[5465,6099,6100],{},"skinparam backgroundColor #FFFFFF\n",[5465,6102,6103],{"class":5467,"line":5507},[5465,6104,6105],{},"skinparam timeline {\n",[5465,6107,6108],{"class":5467,"line":5519},[5465,6109,6110],{},"    BackgroundColor #E2E8F0\n",[5465,6112,6113],{"class":5467,"line":5525},[5465,6114,5522],{},[5465,6116,6117],{"class":5467,"line":5532},[5465,6118,5529],{"emptyLinePlaceholder":5528},[5465,6120,6121],{"class":5467,"line":5565},[5465,6122,6123],{},"scale 1.2\n",[5465,6125,6126],{"class":5467,"line":5594},[5465,6127,5529],{"emptyLinePlaceholder":5528},[5465,6129,6130],{"class":5467,"line":5600},[5465,6131,6132],{},"|Епоха мейнфреймів|\n",[5465,6134,6135],{"class":5467,"line":5624},[5465,6136,6137],{},":1970–1980\n",[5465,6139,6140],{"class":5467,"line":5639},[5465,6141,6142],{},"Sun RPC \u002F ONC RPC;\n",[5465,6144,6145],{"class":5467,"line":5647},[5465,6146,6147],{},"note right #FEF3C7\n",[5465,6149,6150],{"class":5467,"line":5666},[5465,6151,6152],{},"  - Мова C, Unix\n",[5465,6154,6155],{"class":5467,"line":5672},[5465,6156,6157],{},"  - XDR серіалізація\n",[5465,6159,6160],{"class":5467,"line":5690},[5465,6161,6162],{},"  - NFS, RPC Compiler\n",[5465,6164,6165],{"class":5467,"line":5699},[5465,6166,6167],{},"end note\n",[5465,6169,6170],{"class":5467,"line":5705},[5465,6171,5529],{"emptyLinePlaceholder":5528},[5465,6173,6174],{"class":5467,"line":5727},[5465,6175,6176],{},"|Об'єктно-орієнтована ера|\n",[5465,6178,6179],{"class":5467,"line":5732},[5465,6180,6181],{},":1990–2000\n",[5465,6183,6184],{"class":5467,"line":5755},[5465,6185,6186],{},"CORBA \u002F DCOM;\n",[5465,6188,6189],{"class":5467,"line":5786},[5465,6190,6191],{},"note right #FECACA\n",[5465,6193,6194],{"class":5467,"line":5831},[5465,6195,6196],{},"  - Надзвичайна складність\n",[5465,6198,6199],{"class":5467,"line":5836},[5465,6200,6201],{},"  - IDL, ORB, Name Service\n",[5465,6203,6204],{"class":5467,"line":5841},[5465,6205,6206],{},"  - Міжмовна інтеграція\n",[5465,6208,6209],{"class":5467,"line":5850},[5465,6210,6167],{},[5465,6212,6214],{"class":5467,"line":6213},27,[5465,6215,5529],{"emptyLinePlaceholder":5528},[5465,6217,6219],{"class":5467,"line":6218},28,[5465,6220,6221],{},"|Епоха веб-сервісів|\n",[5465,6223,6225],{"class":5467,"line":6224},29,[5465,6226,6227],{},":2000–2010\n",[5465,6229,6231],{"class":5467,"line":6230},30,[5465,6232,6233],{},"XML-RPC → SOAP;\n",[5465,6235,6237],{"class":5467,"line":6236},31,[5465,6238,6239],{},"note right #DBEAFE\n",[5465,6241,6243],{"class":5467,"line":6242},32,[5465,6244,6245],{},"  - XML серіалізація\n",[5465,6247,6249],{"class":5467,"line":6248},33,[5465,6250,6251],{},"  - WS-* стандарти\n",[5465,6253,6255],{"class":5467,"line":6254},34,[5465,6256,6257],{},"  - Корпоративні системи\n",[5465,6259,6261],{"class":5467,"line":6260},35,[5465,6262,6167],{},[5465,6264,6266],{"class":5467,"line":6265},36,[5465,6267,5529],{"emptyLinePlaceholder":5528},[5465,6269,6271],{"class":5467,"line":6270},37,[5465,6272,6273],{},":2007–2009\n",[5465,6275,6277],{"class":5467,"line":6276},38,[5465,6278,6279],{},"Apache Thrift, Avro;\n",[5465,6281,6283],{"class":5467,"line":6282},39,[5465,6284,6285],{},"note right #DCFCE7\n",[5465,6287,6289],{"class":5467,"line":6288},40,[5465,6290,6291],{},"  - Бінарна серіалізація\n",[5465,6293,6295],{"class":5467,"line":6294},41,[5465,6296,6297],{},"  - Facebook, LinkedIn\n",[5465,6299,6301],{"class":5467,"line":6300},42,[5465,6302,6303],{},"  - Hadoop екосистема\n",[5465,6305,6307],{"class":5467,"line":6306},43,[5465,6308,6167],{},[5465,6310,6312],{"class":5467,"line":6311},44,[5465,6313,5529],{"emptyLinePlaceholder":5528},[5465,6315,6317],{"class":5467,"line":6316},45,[5465,6318,6319],{},"|Епоха мікросервісів|\n",[5465,6321,6323],{"class":5467,"line":6322},46,[5465,6324,6325],{},":2015–теперішній час\n",[5465,6327,6329],{"class":5467,"line":6328},47,[5465,6330,6331],{},"**gRPC + Protobuf**;\n",[5465,6333,6335],{"class":5467,"line":6334},48,[5465,6336,6337],{},"note right #D4F4DD\n",[5465,6339,6341],{"class":5467,"line":6340},49,[5465,6342,6343],{},"  - HTTP\u002F2 транспорт\n",[5465,6345,6347],{"class":5467,"line":6346},50,[5465,6348,6349],{},"  - Потокові виклики\n",[5465,6351,6353],{"class":5467,"line":6352},51,[5465,6354,6355],{},"  - Service Mesh ready\n",[5465,6357,6359],{"class":5467,"line":6358},52,[5465,6360,6361],{},"  - Кросплатформенність\n",[5465,6363,6365],{"class":5467,"line":6364},53,[5465,6366,6167],{},[5465,6368,6370],{"class":5467,"line":6369},54,[5465,6371,5529],{"emptyLinePlaceholder":5528},[5465,6373,6375],{"class":5467,"line":6374},55,[5465,6376,6377],{},"@enduml\n",[5267,6379],{},[5270,6381,6383],{"id":6382},"protocol-buffers-мова-опису-контрактів-та-бінарна-серіалізація","Protocol Buffers: мова опису контрактів та бінарна серіалізація",[5275,6385,6387],{"id":6386},"філософія-та-архітектура-protocol-buffers","Філософія та архітектура Protocol Buffers",[5280,6389,6390,6393,6394,6397,6398,6401],{},[5232,6391,6392],{},"Protocol Buffers"," (скорочено ",[5232,6395,6396],{},"Protobuf",") — це мова опису структур даних (",[5284,6399,6400],{},"Interface Definition Language, IDL",") та формат бінарної серіалізації, розроблений компанією Google. Protobuf вирішує дві фундаментальні задачі:",[6403,6404,6405,6418],"ol",{},[5201,6406,6407,6410,6411,6413,6414,6417],{},[5232,6408,6409],{},"Декларативне визначення контрактів"," між клієнтом та сервером у формі файлів ",[5211,6412,5213],{},", які є джерелом істини (",[5284,6415,6416],{},"single source of truth",") для структури даних.",[5201,6419,6420,6423],{},[5232,6421,6422],{},"Високоефективна бінарна серіалізація",", що забезпечує компактне представлення даних у мережі та швидку серіалізацію\u002Fдесеріалізацію.",[5280,6425,6426,6427,5321,6430,6432,6433,5321,6436,6439,6440,5321,6443,6446],{},"На відміну від JSON або XML, де структура даних визначається неявно через приклади або документацію, Protobuf вимагає ",[5232,6428,6429],{},"явного опису схеми",[5284,6431,5308],{},"). Ця схема компілюється за допомогою утиліти ",[5211,6434,6435],{},"protoc",[5284,6437,6438],{},"Protocol Buffers Compiler",") у вихідний код обраної мови програмування (Go, Java, C++, Python тощо). Згенерований код містить структури даних, методи серіалізації та валідації, що гарантує ",[5232,6441,6442],{},"повну сумісність між клієнтом та сервером на етапі компіляції",[5284,6444,6445],{},"compile-time type safety",").",[5275,6448,6450],{"id":6449},"синтаксис-файлів-proto-базові-конструкції","Синтаксис файлів .proto: базові конструкції",[5280,6452,6453,6454,6456,6457,6460,6461,6464,6465,6468],{},"Файли Protocol Buffers мають розширення ",[5211,6455,5213],{}," та використовують декларативний синтаксис для опису повідомлень (",[5284,6458,6459],{},"messages","), перелічень (",[5284,6462,6463],{},"enums",") та сервісів (",[5284,6466,6467],{},"services","). Розглянемо базові конструкції на прикладі системи управління користувачами.",[5456,6470,6474],{"className":6471,"code":6472,"language":6473,"meta":5461,"style":5461},"language-protobuf shiki shiki-themes light-plus dark-plus dark-plus","\u002F\u002F user.proto\nsyntax = \"proto3\";\n\npackage userservice;\n\noption go_package = \"github.com\u002Fexample\u002Fuserservice\u002Fpb\";\n\n\u002F\u002F Повідомлення User описує структуру даних користувача\nmessage User {\n  \u002F\u002F Кожне поле має унікальний числовий ідентифікатор (field number)\n  int64 id = 1;\n  string name = 2;\n  string email = 3;\n  UserStatus status = 4;\n  int64 created_at = 5;\n}\n\n\u002F\u002F Перелічення можливих статусів користувача\nenum UserStatus {\n  USER_STATUS_UNSPECIFIED = 0;  \u002F\u002F Нульове значення обов'язкове у proto3\n  USER_STATUS_ACTIVE = 1;\n  USER_STATUS_SUSPENDED = 2;\n  USER_STATUS_DELETED = 3;\n}\n\n\u002F\u002F Запит на створення користувача\nmessage CreateUserRequest {\n  string name = 1;\n  string email = 2;\n}\n\n\u002F\u002F Відповідь на створення користувача\nmessage CreateUserResponse {\n  User user = 1;\n}\n\n\u002F\u002F Запит на отримання користувача за ідентифікатором\nmessage GetUserRequest {\n  int64 id = 1;\n}\n\n\u002F\u002F Відповідь з даними користувача\nmessage GetUserResponse {\n  User user = 1;\n}\n","protobuf",[5211,6475,6476,6481,6486,6490,6495,6499,6504,6508,6513,6518,6523,6528,6533,6538,6543,6548,6552,6556,6561,6566,6571,6576,6581,6586,6590,6594,6599,6604,6609,6614,6618,6622,6627,6632,6637,6641,6645,6650,6655,6659,6663,6667,6672,6677,6681],{"__ignoreMap":5461},[5465,6477,6478],{"class":5467,"line":5468},[5465,6479,6480],{},"\u002F\u002F user.proto\n",[5465,6482,6483],{"class":5467,"line":5475},[5465,6484,6485],{},"syntax = \"proto3\";\n",[5465,6487,6488],{"class":5467,"line":5493},[5465,6489,5529],{"emptyLinePlaceholder":5528},[5465,6491,6492],{"class":5467,"line":5507},[5465,6493,6494],{},"package userservice;\n",[5465,6496,6497],{"class":5467,"line":5519},[5465,6498,5529],{"emptyLinePlaceholder":5528},[5465,6500,6501],{"class":5467,"line":5525},[5465,6502,6503],{},"option go_package = \"github.com\u002Fexample\u002Fuserservice\u002Fpb\";\n",[5465,6505,6506],{"class":5467,"line":5532},[5465,6507,5529],{"emptyLinePlaceholder":5528},[5465,6509,6510],{"class":5467,"line":5565},[5465,6511,6512],{},"\u002F\u002F Повідомлення User описує структуру даних користувача\n",[5465,6514,6515],{"class":5467,"line":5594},[5465,6516,6517],{},"message User {\n",[5465,6519,6520],{"class":5467,"line":5600},[5465,6521,6522],{},"  \u002F\u002F Кожне поле має унікальний числовий ідентифікатор (field number)\n",[5465,6524,6525],{"class":5467,"line":5624},[5465,6526,6527],{},"  int64 id = 1;\n",[5465,6529,6530],{"class":5467,"line":5639},[5465,6531,6532],{},"  string name = 2;\n",[5465,6534,6535],{"class":5467,"line":5647},[5465,6536,6537],{},"  string email = 3;\n",[5465,6539,6540],{"class":5467,"line":5666},[5465,6541,6542],{},"  UserStatus status = 4;\n",[5465,6544,6545],{"class":5467,"line":5672},[5465,6546,6547],{},"  int64 created_at = 5;\n",[5465,6549,6550],{"class":5467,"line":5690},[5465,6551,5522],{},[5465,6553,6554],{"class":5467,"line":5699},[5465,6555,5529],{"emptyLinePlaceholder":5528},[5465,6557,6558],{"class":5467,"line":5705},[5465,6559,6560],{},"\u002F\u002F Перелічення можливих статусів користувача\n",[5465,6562,6563],{"class":5467,"line":5727},[5465,6564,6565],{},"enum UserStatus {\n",[5465,6567,6568],{"class":5467,"line":5732},[5465,6569,6570],{},"  USER_STATUS_UNSPECIFIED = 0;  \u002F\u002F Нульове значення обов'язкове у proto3\n",[5465,6572,6573],{"class":5467,"line":5755},[5465,6574,6575],{},"  USER_STATUS_ACTIVE = 1;\n",[5465,6577,6578],{"class":5467,"line":5786},[5465,6579,6580],{},"  USER_STATUS_SUSPENDED = 2;\n",[5465,6582,6583],{"class":5467,"line":5831},[5465,6584,6585],{},"  USER_STATUS_DELETED = 3;\n",[5465,6587,6588],{"class":5467,"line":5836},[5465,6589,5522],{},[5465,6591,6592],{"class":5467,"line":5841},[5465,6593,5529],{"emptyLinePlaceholder":5528},[5465,6595,6596],{"class":5467,"line":5850},[5465,6597,6598],{},"\u002F\u002F Запит на створення користувача\n",[5465,6600,6601],{"class":5467,"line":6213},[5465,6602,6603],{},"message CreateUserRequest {\n",[5465,6605,6606],{"class":5467,"line":6218},[5465,6607,6608],{},"  string name = 1;\n",[5465,6610,6611],{"class":5467,"line":6224},[5465,6612,6613],{},"  string email = 2;\n",[5465,6615,6616],{"class":5467,"line":6230},[5465,6617,5522],{},[5465,6619,6620],{"class":5467,"line":6236},[5465,6621,5529],{"emptyLinePlaceholder":5528},[5465,6623,6624],{"class":5467,"line":6242},[5465,6625,6626],{},"\u002F\u002F Відповідь на створення користувача\n",[5465,6628,6629],{"class":5467,"line":6248},[5465,6630,6631],{},"message CreateUserResponse {\n",[5465,6633,6634],{"class":5467,"line":6254},[5465,6635,6636],{},"  User user = 1;\n",[5465,6638,6639],{"class":5467,"line":6260},[5465,6640,5522],{},[5465,6642,6643],{"class":5467,"line":6265},[5465,6644,5529],{"emptyLinePlaceholder":5528},[5465,6646,6647],{"class":5467,"line":6270},[5465,6648,6649],{},"\u002F\u002F Запит на отримання користувача за ідентифікатором\n",[5465,6651,6652],{"class":5467,"line":6276},[5465,6653,6654],{},"message GetUserRequest {\n",[5465,6656,6657],{"class":5467,"line":6282},[5465,6658,6527],{},[5465,6660,6661],{"class":5467,"line":6288},[5465,6662,5522],{},[5465,6664,6665],{"class":5467,"line":6294},[5465,6666,5529],{"emptyLinePlaceholder":5528},[5465,6668,6669],{"class":5467,"line":6300},[5465,6670,6671],{},"\u002F\u002F Відповідь з даними користувача\n",[5465,6673,6674],{"class":5467,"line":6306},[5465,6675,6676],{},"message GetUserResponse {\n",[5465,6678,6679],{"class":5467,"line":6311},[5465,6680,6636],{},[5465,6682,6683],{"class":5467,"line":6316},[5465,6684,5522],{},[5280,6686,6687],{},[5232,6688,6689],{},"Покрокове пояснення синтаксису:",[5280,6691,6692],{},[5232,6693,6694,6695],{},"Рядок 2: ",[5211,6696,6697],{},"syntax = \"proto3\";",[5280,6699,6700,6701,6704,6705,6708,6709,6712,6713,5214],{},"Вказує версію синтаксису Protocol Buffers. Версія ",[5211,6702,6703],{},"proto3"," є поточною стабільною версією, що спрощує синтаксис порівняно з ",[5211,6706,6707],{},"proto2"," (видалені обов'язкові поля ",[5211,6710,6711],{},"required",", спрощена обробка значень за замовчуванням). Усі нові проєкти повинні використовувати ",[5211,6714,6703],{},[5280,6716,6717],{},[5232,6718,6719,6720],{},"Рядок 4: ",[5211,6721,6722],{},"package userservice;",[5280,6724,6725,6726,6728],{},"Визначає простір імен для згенерованого коду. У Go це буде перетворено на назву пакета, у Java — на Java package, у C++ — на namespace. Це запобігає колізіям імен при імпорті кількох ",[5211,6727,5213],{}," файлів.",[5280,6730,6731],{},[5232,6732,6733,6734],{},"Рядок 6: ",[5211,6735,6736],{},"option go_package = \"...\";",[5280,6738,6739],{},"Вказує повний шлях імпорту для згенерованого Go-коду. Це критично важливо для коректної роботи Go Modules. Значення має відповідати структурі вашого репозиторію.",[5280,6741,6742],{},[5232,6743,6744,6745],{},"Рядки 9–16: Повідомлення ",[5211,6746,6747],{},"message User",[5280,6749,6750],{},"Описує структуру даних користувача. Кожне поле має:",[5198,6752,6753,6769,6784],{},[5201,6754,6755,6758,6759,5336,6762,5336,6765,6768],{},[5232,6756,6757],{},"Тип даних:"," ",[5211,6760,6761],{},"int64",[5211,6763,6764],{},"string",[5211,6766,6767],{},"UserStatus"," (власний enum), тощо.",[5201,6770,6771,6758,6774,5336,6777,5336,6780,6783],{},[5232,6772,6773],{},"Ім'я поля:",[5211,6775,6776],{},"id",[5211,6778,6779],{},"name",[5211,6781,6782],{},"email"," — використовується у згенерованому коді.",[5201,6785,6786,6758,6789,5336,6792,5336,6795,6798,6799,6802],{},[5232,6787,6788],{},"Числовий ідентифікатор (field number):",[5211,6790,6791],{},"= 1",[5211,6793,6794],{},"= 2",[5211,6796,6797],{},"= 3"," — унікальний номер, що ідентифікує поле у бінарному форматі. ",[5232,6800,6801],{},"Критично важливо:"," після публікації схеми ці номери не можна змінювати, оскільки це порушить сумісність зі старими клієнтами.",[5280,6804,6805],{},[5232,6806,6807,6808],{},"Рядки 19–24: Перелічення ",[5211,6809,6810],{},"enum UserStatus",[5280,6812,6813,6814,6817,6818,6821,6822,6825],{},"Визначає набір іменованих констант. ",[5232,6815,6816],{},"Обов'язкова вимога proto3:"," перше значення enum повинно мати номер ",[5211,6819,6820],{},"0"," та зазвичай називається ",[5211,6823,6824],{},"*_UNSPECIFIED",", що представляє невстановлене значення.",[5280,6827,6828],{},[5232,6829,6830],{},"Рядки 27–39: Повідомлення запитів та відповідей",[5280,6832,6833,6834,6837],{},"Для кожної операції RPC визначаються окремі типи запитів та відповідей. Це забезпечує можливість розширення API у майбутньому без порушення зворотної сумісності. Наприклад, якщо пізніше потрібно додати параметр фільтрації до ",[5211,6835,6836],{},"GetUserRequest",", можна додати нове поле без впливу на існуючих клієнтів.",[6839,6840,6841,6842,5336,6844,6846],"tip",{},"Завжди використовуйте окремі типи повідомлень для запитів та відповідей кожного RPC-методу, навіть якщо вони містять лише одне поле. Це дозволить у майбутньому розширювати API, додаючи нові параметри або метадані, без порушення існуючих клієнтів. Ніколи не використовуйте голі скалярні типи (",[5211,6843,6761],{},[5211,6845,6764],{},") як параметри RPC-методів.",[5275,6848,6850],{"id":6849},"скалярні-типи-даних-у-protocol-buffers","Скалярні типи даних у Protocol Buffers",[5280,6852,6853],{},"Protocol Buffers підтримує багатий набір скалярних типів даних, що відображаються на відповідні типи у різних мовах програмування. Розглянемо найпоширеніші типи та їх еквіваленти у Go.",[6855,6856,6857,6876],"table",{},[6858,6859,6860],"thead",{},[6861,6862,6863,6867,6870,6873],"tr",{},[6864,6865,6866],"th",{},"Тип у .proto",[6864,6868,6869],{},"Тип у Go",[6864,6871,6872],{},"Опис",[6864,6874,6875],{},"Приклад використання",[6877,6878,6879,6901,6923,6945,6967,6984,7000,7018],"tbody",{},[6861,6880,6881,6889,6895,6898],{},[6882,6883,6884,5336,6887],"td",{},[5211,6885,6886],{},"int32",[5211,6888,6761],{},[6882,6890,6891,5336,6893],{},[5211,6892,6886],{},[5211,6894,6761],{},[6882,6896,6897],{},"Знакові цілі числа змінної довжини (variable-length encoding)",[6882,6899,6900],{},"Ідентифікатори, лічильники",[6861,6902,6903,6911,6917,6920],{},[6882,6904,6905,5336,6908],{},[5211,6906,6907],{},"uint32",[5211,6909,6910],{},"uint64",[6882,6912,6913,5336,6915],{},[5211,6914,6907],{},[5211,6916,6910],{},[6882,6918,6919],{},"Беззнакові цілі числа",[6882,6921,6922],{},"Розміри, timestamps",[6861,6924,6925,6933,6939,6942],{},[6882,6926,6927,5336,6930],{},[5211,6928,6929],{},"sint32",[5211,6931,6932],{},"sint64",[6882,6934,6935,5336,6937],{},[5211,6936,6886],{},[5211,6938,6761],{},[6882,6940,6941],{},"Знакові числа з ефективним кодуванням від'ємних значень",[6882,6943,6944],{},"Координати, різниці значень",[6861,6946,6947,6955,6961,6964],{},[6882,6948,6949,5336,6952],{},[5211,6950,6951],{},"fixed32",[5211,6953,6954],{},"fixed64",[6882,6956,6957,5336,6959],{},[5211,6958,6907],{},[5211,6960,6910],{},[6882,6962,6963],{},"Фіксовані 4\u002F8 байтів (ефективні для великих чисел > 2^28)",[6882,6965,6966],{},"Хеші, великі ID",[6861,6968,6969,6974,6978,6981],{},[6882,6970,6971],{},[5211,6972,6973],{},"bool",[6882,6975,6976],{},[5211,6977,6973],{},[6882,6979,6980],{},"Логічний тип",[6882,6982,6983],{},"Прапорці, стани",[6861,6985,6986,6990,6994,6997],{},[6882,6987,6988],{},[5211,6989,6764],{},[6882,6991,6992],{},[5211,6993,6764],{},[6882,6995,6996],{},"UTF-8 або 7-bit ASCII текст",[6882,6998,6999],{},"Імена, описи, email",[6861,7001,7002,7007,7012,7015],{},[6882,7003,7004],{},[5211,7005,7006],{},"bytes",[6882,7008,7009],{},[5211,7010,7011],{},"[]byte",[6882,7013,7014],{},"Довільна послідовність байтів",[6882,7016,7017],{},"Зображення, файли, бінарні дані",[6861,7019,7020,7028,7036,7039],{},[6882,7021,7022,5336,7025],{},[5211,7023,7024],{},"float",[5211,7026,7027],{},"double",[6882,7029,7030,5336,7033],{},[5211,7031,7032],{},"float32",[5211,7034,7035],{},"float64",[6882,7037,7038],{},"Числа з плаваючою комою",[6882,7040,7041],{},"Ціни, координати GPS",[7043,7044,7045,7046,7048,7049,7051,7052,5336,7054,5336,7056,5336,7058,7060,7061,7064],"warning",{},"Уникайте використання типів ",[5211,7047,6951],{}," та ",[5211,7050,6954],{}," для малих чисел (\u003C 2^28), оскільки вони завжди займають 4 або 8 байтів незалежно від значення. Для малих чисел типи ",[5211,7053,6886],{},[5211,7055,6761],{},[5211,7057,6907],{},[5211,7059,6910],{}," використовують кодування змінної довжини (",[5284,7062,7063],{},"varint","), що зменшує розмір повідомлення у 2–10 разів.",[5275,7066,7068],{"id":7067},"складні-типи-даних-вкладені-повідомлення-списки-та-мапи","Складні типи даних: вкладені повідомлення, списки та мапи",[5280,7070,7071,7072,7075,7076,6446],{},"Protocol Buffers підтримує композицію складних структур даних через вкладені повідомлення, повторювані поля (",[5284,7073,7074],{},"repeated",") та асоціативні масиви (",[5284,7077,7078],{},"map",[5456,7080,7082],{"className":6471,"code":7081,"language":6473,"meta":5461,"style":5461},"syntax = \"proto3\";\n\npackage ecommerce;\n\n\u002F\u002F Адреса доставки\nmessage Address {\n  string street = 1;\n  string city = 2;\n  string postal_code = 3;\n  string country = 4;\n}\n\n\u002F\u002F Товар у кошику\nmessage CartItem {\n  int64 product_id = 1;\n  string product_name = 2;\n  int32 quantity = 3;\n  double unit_price = 4;\n}\n\n\u002F\u002F Замовлення\nmessage Order {\n  int64 order_id = 1;\n  int64 user_id = 2;\n  \n  \u002F\u002F Вкладене повідомлення\n  Address shipping_address = 3;\n  \n  \u002F\u002F Список товарів (repeated field)\n  repeated CartItem items = 4;\n  \n  \u002F\u002F Мапа додаткових метаданих (ключ-значення)\n  map\u003Cstring, string> metadata = 5;\n  \n  \u002F\u002F Часова мітка створення (Unix timestamp у секундах)\n  int64 created_at = 6;\n  \n  \u002F\u002F Загальна сума замовлення\n  double total_amount = 7;\n}\n",[5211,7083,7084,7088,7092,7097,7101,7106,7111,7116,7121,7126,7131,7135,7139,7144,7149,7154,7159,7164,7169,7173,7177,7182,7187,7192,7197,7202,7207,7212,7216,7221,7226,7230,7235,7240,7244,7249,7254,7258,7263,7268],{"__ignoreMap":5461},[5465,7085,7086],{"class":5467,"line":5468},[5465,7087,6485],{},[5465,7089,7090],{"class":5467,"line":5475},[5465,7091,5529],{"emptyLinePlaceholder":5528},[5465,7093,7094],{"class":5467,"line":5493},[5465,7095,7096],{},"package ecommerce;\n",[5465,7098,7099],{"class":5467,"line":5507},[5465,7100,5529],{"emptyLinePlaceholder":5528},[5465,7102,7103],{"class":5467,"line":5519},[5465,7104,7105],{},"\u002F\u002F Адреса доставки\n",[5465,7107,7108],{"class":5467,"line":5525},[5465,7109,7110],{},"message Address {\n",[5465,7112,7113],{"class":5467,"line":5532},[5465,7114,7115],{},"  string street = 1;\n",[5465,7117,7118],{"class":5467,"line":5565},[5465,7119,7120],{},"  string city = 2;\n",[5465,7122,7123],{"class":5467,"line":5594},[5465,7124,7125],{},"  string postal_code = 3;\n",[5465,7127,7128],{"class":5467,"line":5600},[5465,7129,7130],{},"  string country = 4;\n",[5465,7132,7133],{"class":5467,"line":5624},[5465,7134,5522],{},[5465,7136,7137],{"class":5467,"line":5639},[5465,7138,5529],{"emptyLinePlaceholder":5528},[5465,7140,7141],{"class":5467,"line":5647},[5465,7142,7143],{},"\u002F\u002F Товар у кошику\n",[5465,7145,7146],{"class":5467,"line":5666},[5465,7147,7148],{},"message CartItem {\n",[5465,7150,7151],{"class":5467,"line":5672},[5465,7152,7153],{},"  int64 product_id = 1;\n",[5465,7155,7156],{"class":5467,"line":5690},[5465,7157,7158],{},"  string product_name = 2;\n",[5465,7160,7161],{"class":5467,"line":5699},[5465,7162,7163],{},"  int32 quantity = 3;\n",[5465,7165,7166],{"class":5467,"line":5705},[5465,7167,7168],{},"  double unit_price = 4;\n",[5465,7170,7171],{"class":5467,"line":5727},[5465,7172,5522],{},[5465,7174,7175],{"class":5467,"line":5732},[5465,7176,5529],{"emptyLinePlaceholder":5528},[5465,7178,7179],{"class":5467,"line":5755},[5465,7180,7181],{},"\u002F\u002F Замовлення\n",[5465,7183,7184],{"class":5467,"line":5786},[5465,7185,7186],{},"message Order {\n",[5465,7188,7189],{"class":5467,"line":5831},[5465,7190,7191],{},"  int64 order_id = 1;\n",[5465,7193,7194],{"class":5467,"line":5836},[5465,7195,7196],{},"  int64 user_id = 2;\n",[5465,7198,7199],{"class":5467,"line":5841},[5465,7200,7201],{},"  \n",[5465,7203,7204],{"class":5467,"line":5850},[5465,7205,7206],{},"  \u002F\u002F Вкладене повідомлення\n",[5465,7208,7209],{"class":5467,"line":6213},[5465,7210,7211],{},"  Address shipping_address = 3;\n",[5465,7213,7214],{"class":5467,"line":6218},[5465,7215,7201],{},[5465,7217,7218],{"class":5467,"line":6224},[5465,7219,7220],{},"  \u002F\u002F Список товарів (repeated field)\n",[5465,7222,7223],{"class":5467,"line":6230},[5465,7224,7225],{},"  repeated CartItem items = 4;\n",[5465,7227,7228],{"class":5467,"line":6236},[5465,7229,7201],{},[5465,7231,7232],{"class":5467,"line":6242},[5465,7233,7234],{},"  \u002F\u002F Мапа додаткових метаданих (ключ-значення)\n",[5465,7236,7237],{"class":5467,"line":6248},[5465,7238,7239],{},"  map\u003Cstring, string> metadata = 5;\n",[5465,7241,7242],{"class":5467,"line":6254},[5465,7243,7201],{},[5465,7245,7246],{"class":5467,"line":6260},[5465,7247,7248],{},"  \u002F\u002F Часова мітка створення (Unix timestamp у секундах)\n",[5465,7250,7251],{"class":5467,"line":6265},[5465,7252,7253],{},"  int64 created_at = 6;\n",[5465,7255,7256],{"class":5467,"line":6270},[5465,7257,7201],{},[5465,7259,7260],{"class":5467,"line":6276},[5465,7261,7262],{},"  \u002F\u002F Загальна сума замовлення\n",[5465,7264,7265],{"class":5467,"line":6282},[5465,7266,7267],{},"  double total_amount = 7;\n",[5465,7269,7270],{"class":5467,"line":6288},[5465,7271,5522],{},[5280,7273,7274],{},[5232,7275,7276],{},"Repeated fields (повторювані поля):",[5280,7278,7279,7280,7282,7283,7286],{},"Поле з модифікатором ",[5211,7281,7074],{}," представляє упорядкований список значень змінної довжини. У Go це відображається на зріз (",[5211,7284,7285],{},"[]CartItem","). Порядок елементів зберігається при серіалізації.",[5280,7288,7289],{},[5232,7290,7291],{},"Map fields (асоціативні масиви):",[5280,7293,7294,7295,7298,7299,5336,7301,7048,7303,7305,7306,7309],{},"Синтаксис ",[5211,7296,7297],{},"map\u003CKeyType, ValueType>"," визначає асоціативний масив. Тип ключа може бути будь-яким скалярним типом крім ",[5211,7300,7024],{},[5211,7302,7027],{},[5211,7304,7006],{},". Тип значення може бути будь-яким, включно з вкладеними повідомленнями. У Go відображається на ",[5211,7307,7308],{},"map[string]string"," або відповідний тип.",[5430,7311,7312,7313,7315,7316,5321,7319,7322,7323,7325,7326,7329,7330,7333,7334,7336,7337,7340],{},"У версії ",[5211,7314,6703],{}," усі поля є опціональними за замовчуванням. Якщо поле не встановлено при серіалізації, воно не потрапляє у бінарне представлення (економія місця). При десеріалізації відсутні поля отримують ",[5232,7317,7318],{},"нульові значення",[5284,7320,7321],{},"zero values","): ",[5211,7324,6820],{}," для чисел, ",[5211,7327,7328],{},"\"\""," для рядків, ",[5211,7331,7332],{},"false"," для bool, ",[5211,7335,5685],{}," для повідомлень. Це означає, що неможливо розрізнити «поле явно встановлено у нуль» та «поле не встановлено». Для вирішення цієї проблеми у proto3 існує модифікатор ",[5211,7338,7339],{},"optional",", що генерує покажчики у Go.",[5275,7342,7344],{"id":7343},"компіляція-proto-файлів-та-генерація-коду","Компіляція .proto файлів та генерація коду",[5280,7346,7347,7348,7350,7351,5321,7355,7357],{},"Файли ",[5211,7349,5213],{}," є джерелом для генерації коду на різних мовах програмування. Для компіляції використовується утиліта ",[5232,7352,7353],{},[5211,7354,6435],{},[5284,7356,6438],{},") разом із мовно-специфічними плагінами.",[5190,7359,7360,7474],{},[5193,7361,7363,7371,7406,7411,7441,7446],{"icon":4409,"title":7362},"📦 Необхідні пакети та інструменти",[5280,7364,7365],{},[5232,7366,7367,7368,7370],{},"Protocol Buffers Compiler (",[5211,7369,6435],{},")",[5198,7372,7373,7385,7391,7400],{},[5201,7374,7375,6758,7378],{},[5232,7376,7377],{},"Репозиторій:",[7379,7380,7384],"a",{"href":7381,"rel":7382},"https:\u002F\u002Fgithub.com\u002Fprotocolbuffers\u002Fprotobuf",[7383],"nofollow","github.com\u002Fprotocolbuffers\u002Fprotobuf",[5201,7386,7387,7390],{},[5232,7388,7389],{},"Версія:"," >= 3.15.0 (рекомендовано >= 25.0)",[5201,7392,7393,7396,7397,7399],{},[5232,7394,7395],{},"Призначення:"," Компіляція ",[5211,7398,5213],{}," файлів у вихідний код різних мов програмування",[5201,7401,7402,7405],{},[5232,7403,7404],{},"Платформи:"," Linux, macOS, Windows",[5280,7407,7408],{},[5232,7409,7410],{},"protoc-gen-go (Go Plugin для Protobuf)",[5198,7412,7413,7421,7426,7431],{},[5201,7414,7415,6758,7418],{},[5232,7416,7417],{},"Пакет:",[5211,7419,7420],{},"google.golang.org\u002Fprotobuf\u002Fcmd\u002Fprotoc-gen-go",[5201,7422,7423,7425],{},[5232,7424,7389],{}," >= v1.28",[5201,7427,7428,7430],{},[5232,7429,7395],{}," Генерація Go-структур для Protobuf повідомлень",[5201,7432,7433,6758,7436],{},[5232,7434,7435],{},"Документація:",[7379,7437,7440],{"href":7438,"rel":7439},"https:\u002F\u002Fprotobuf.dev\u002Freference\u002Fgo\u002Fgo-generated",[7383],"protobuf.dev\u002Freference\u002Fgo\u002Fgo-generated",[5280,7442,7443],{},[5232,7444,7445],{},"protoc-gen-go-grpc (Go Plugin для gRPC)",[5198,7447,7448,7455,7460,7465],{},[5201,7449,7450,6758,7452],{},[5232,7451,7417],{},[5211,7453,7454],{},"google.golang.org\u002Fgrpc\u002Fcmd\u002Fprotoc-gen-go-grpc",[5201,7456,7457,7459],{},[5232,7458,7389],{}," >= v1.2",[5201,7461,7462,7464],{},[5232,7463,7395],{}," Генерація gRPC серверних інтерфейсів та клієнтських stubs",[5201,7466,7467,6758,7469],{},[5232,7468,7435],{},[7379,7470,7473],{"href":7471,"rel":7472},"https:\u002F\u002Fgrpc.io\u002Fdocs\u002Flanguages\u002Fgo\u002Fgenerated-code\u002F",[7383],"grpc.io\u002Fdocs\u002Flanguages\u002Fgo\u002Fgenerated-code",[5193,7475,7478,7591,7596,7603,7614,7621],{"icon":7476,"title":7477},"i-lucide-settings","🔧 Основні залежності Go-проєкту",[5456,7479,7481],{"className":5458,"code":7480,"language":5460,"meta":5461,"style":5461},"\u002F\u002F go.mod\nmodule github.com\u002Fexample\u002Fuserservice\n\ngo 1.21\n\nrequire (\n    google.golang.org\u002Fgrpc v1.60.0\n    google.golang.org\u002Fprotobuf v1.32.0\n)\n",[5211,7482,7483,7488,7512,7516,7524,7528,7536,7564,7587],{"__ignoreMap":5461},[5465,7484,7485],{"class":5467,"line":5468},[5465,7486,7487],{"class":5471},"\u002F\u002F go.mod\n",[5465,7489,7490,7493,7496,7498,7501,7504,7507,7509],{"class":5467,"line":5475},[5465,7491,7492],{"class":5496},"module",[5465,7494,7495],{"class":5496}," github",[5465,7497,5214],{"class":5489},[5465,7499,7500],{"class":5496},"com",[5465,7502,7503],{"class":5489},"\u002F",[5465,7505,7506],{"class":5496},"example",[5465,7508,7503],{"class":5489},[5465,7510,7511],{"class":5496},"userservice\n",[5465,7513,7514],{"class":5467,"line":5493},[5465,7515,5529],{"emptyLinePlaceholder":5528},[5465,7517,7518,7520],{"class":5467,"line":5507},[5465,7519,5460],{"class":5675},[5465,7521,7523],{"class":7522},"sJj4R"," 1.21\n",[5465,7525,7526],{"class":5467,"line":5519},[5465,7527,5529],{"emptyLinePlaceholder":5528},[5465,7529,7530,7533],{"class":5467,"line":5525},[5465,7531,7532],{"class":5496},"require",[5465,7534,7535],{"class":5489}," (\n",[5465,7537,7538,7541,7543,7546,7548,7551,7553,7556,7559,7561],{"class":5467,"line":5532},[5465,7539,7540],{"class":5496},"    google",[5465,7542,5214],{"class":5489},[5465,7544,7545],{"class":5496},"golang",[5465,7547,5214],{"class":5489},[5465,7549,7550],{"class":5496},"org",[5465,7552,7503],{"class":5489},[5465,7554,7555],{"class":5496},"grpc",[5465,7557,7558],{"class":5496}," v1",[5465,7560,5214],{"class":5489},[5465,7562,7563],{"class":7522},"60.0\n",[5465,7565,7566,7568,7570,7572,7574,7576,7578,7580,7582,7584],{"class":5467,"line":5565},[5465,7567,7540],{"class":5496},[5465,7569,5214],{"class":5489},[5465,7571,7545],{"class":5496},[5465,7573,5214],{"class":5489},[5465,7575,7550],{"class":5496},[5465,7577,7503],{"class":5489},[5465,7579,6473],{"class":5496},[5465,7581,7558],{"class":5496},[5465,7583,5214],{"class":5489},[5465,7585,7586],{"class":7522},"32.0\n",[5465,7588,7589],{"class":5467,"line":5594},[5465,7590,5591],{"class":5489},[5280,7592,7593],{},[5232,7594,7595],{},"Основні пакети:",[5280,7597,7598],{},[5232,7599,7600],{},[5211,7601,7602],{},"google.golang.org\u002Fgrpc",[5198,7604,7605,7608,7611],{},[5201,7606,7607],{},"Основна бібліотека gRPC для Go",[5201,7609,7610],{},"Містить серверну та клієнтську реалізації",[5201,7612,7613],{},"Підтримку інтерцепторів, метаданих, балансування навантаження",[5280,7615,7616],{},[5232,7617,7618],{},[5211,7619,7620],{},"google.golang.org\u002Fprotobuf",[5198,7622,7623,7626,7632],{},[5201,7624,7625],{},"Нова офіційна реалізація Protocol Buffers для Go",[5201,7627,7628,7629],{},"Замінює застарілий пакет ",[5211,7630,7631],{},"github.com\u002Fgolang\u002Fprotobuf",[5201,7633,7634],{},"Покращена продуктивність та API",[5280,7636,7637],{},[5232,7638,7639],{},"Встановлення необхідних інструментів для Go:",[7641,7642,7643,7746,7809],"tabs",{},[7644,7645,7647],"tabs-item",{"label":7646},"macOS (Homebrew)",[5456,7648,7652],{"className":7649,"code":7650,"language":7651,"meta":5461,"style":5461},"language-bash shiki shiki-themes light-plus dark-plus dark-plus","# Встановлення компілятора protoc\nbrew install protobuf\n\n# Перевірка версії (має бути >= 3.15)\nprotoc --version\n\n# Встановлення плагінів для Go\ngo install google.golang.org\u002Fprotobuf\u002Fcmd\u002Fprotoc-gen-go@latest\ngo install google.golang.org\u002Fgrpc\u002Fcmd\u002Fprotoc-gen-go-grpc@latest\n\n# Додавання $GOPATH\u002Fbin до PATH (якщо ще не додано)\nexport PATH=\"$PATH:$(go env GOPATH)\u002Fbin\"\n","bash",[5211,7653,7654,7659,7670,7674,7679,7686,7690,7695,7704,7713,7717,7722],{"__ignoreMap":5461},[5465,7655,7656],{"class":5467,"line":5468},[5465,7657,7658],{"class":5471},"# Встановлення компілятора protoc\n",[5465,7660,7661,7664,7667],{"class":5467,"line":5475},[5465,7662,7663],{"class":5538},"brew",[5465,7665,7666],{"class":5503}," install",[5465,7668,7669],{"class":5503}," protobuf\n",[5465,7671,7672],{"class":5467,"line":5493},[5465,7673,5529],{"emptyLinePlaceholder":5528},[5465,7675,7676],{"class":5467,"line":5507},[5465,7677,7678],{"class":5471},"# Перевірка версії (має бути >= 3.15)\n",[5465,7680,7681,7683],{"class":5467,"line":5519},[5465,7682,6435],{"class":5538},[5465,7684,7685],{"class":5478}," --version\n",[5465,7687,7688],{"class":5467,"line":5525},[5465,7689,5529],{"emptyLinePlaceholder":5528},[5465,7691,7692],{"class":5467,"line":5532},[5465,7693,7694],{"class":5471},"# Встановлення плагінів для Go\n",[5465,7696,7697,7699,7701],{"class":5467,"line":5565},[5465,7698,5460],{"class":5538},[5465,7700,7666],{"class":5503},[5465,7702,7703],{"class":5503}," google.golang.org\u002Fprotobuf\u002Fcmd\u002Fprotoc-gen-go@latest\n",[5465,7705,7706,7708,7710],{"class":5467,"line":5594},[5465,7707,5460],{"class":5538},[5465,7709,7666],{"class":5503},[5465,7711,7712],{"class":5503}," google.golang.org\u002Fgrpc\u002Fcmd\u002Fprotoc-gen-go-grpc@latest\n",[5465,7714,7715],{"class":5467,"line":5600},[5465,7716,5529],{"emptyLinePlaceholder":5528},[5465,7718,7719],{"class":5467,"line":5624},[5465,7720,7721],{"class":5471},"# Додавання $GOPATH\u002Fbin до PATH (якщо ще не додано)\n",[5465,7723,7724,7727,7730,7733,7735,7738,7741,7743],{"class":5467,"line":5639},[5465,7725,7726],{"class":5478},"export",[5465,7728,7729],{"class":5496}," PATH",[5465,7731,7732],{"class":5489},"=",[5465,7734,5813],{"class":5503},[5465,7736,7737],{"class":5496},"$PATH",[5465,7739,7740],{"class":5503},":$(",[5465,7742,5460],{"class":5538},[5465,7744,7745],{"class":5503}," env GOPATH)\u002Fbin\"\n",[7644,7747,7749],{"label":7748},"Linux (apt)",[5456,7750,7752],{"className":7649,"code":7751,"language":7651,"meta":5461,"style":5461},"# Встановлення protoc\nsudo apt update\nsudo apt install -y protobuf-compiler\n\n# Встановлення Go плагінів\ngo install google.golang.org\u002Fprotobuf\u002Fcmd\u002Fprotoc-gen-go@latest\ngo install google.golang.org\u002Fgrpc\u002Fcmd\u002Fprotoc-gen-go-grpc@latest\n",[5211,7753,7754,7759,7770,7784,7788,7793,7801],{"__ignoreMap":5461},[5465,7755,7756],{"class":5467,"line":5468},[5465,7757,7758],{"class":5471},"# Встановлення protoc\n",[5465,7760,7761,7764,7767],{"class":5467,"line":5475},[5465,7762,7763],{"class":5538},"sudo",[5465,7765,7766],{"class":5503}," apt",[5465,7768,7769],{"class":5503}," update\n",[5465,7771,7772,7774,7776,7778,7781],{"class":5467,"line":5493},[5465,7773,7763],{"class":5538},[5465,7775,7766],{"class":5503},[5465,7777,7666],{"class":5503},[5465,7779,7780],{"class":5478}," -y",[5465,7782,7783],{"class":5503}," protobuf-compiler\n",[5465,7785,7786],{"class":5467,"line":5507},[5465,7787,5529],{"emptyLinePlaceholder":5528},[5465,7789,7790],{"class":5467,"line":5519},[5465,7791,7792],{"class":5471},"# Встановлення Go плагінів\n",[5465,7794,7795,7797,7799],{"class":5467,"line":5525},[5465,7796,5460],{"class":5538},[5465,7798,7666],{"class":5503},[5465,7800,7703],{"class":5503},[5465,7802,7803,7805,7807],{"class":5467,"line":5532},[5465,7804,5460],{"class":5538},[5465,7806,7666],{"class":5503},[5465,7808,7712],{"class":5503},[7644,7810,7812],{"label":7811},"Go Modules (універсальний)",[5456,7813,7815],{"className":7649,"code":7814,"language":7651,"meta":5461,"style":5461},"# Встановлення плагінів через Go\ngo install google.golang.org\u002Fprotobuf\u002Fcmd\u002Fprotoc-gen-go@latest\ngo install google.golang.org\u002Fgrpc\u002Fcmd\u002Fprotoc-gen-go-grpc@latest\n\n# Завантаження бінарного protoc з офіційного релізу\n# https:\u002F\u002Fgithub.com\u002Fprotocolbuffers\u002Fprotobuf\u002Freleases\n",[5211,7816,7817,7822,7830,7838,7842,7847],{"__ignoreMap":5461},[5465,7818,7819],{"class":5467,"line":5468},[5465,7820,7821],{"class":5471},"# Встановлення плагінів через Go\n",[5465,7823,7824,7826,7828],{"class":5467,"line":5475},[5465,7825,5460],{"class":5538},[5465,7827,7666],{"class":5503},[5465,7829,7703],{"class":5503},[5465,7831,7832,7834,7836],{"class":5467,"line":5493},[5465,7833,5460],{"class":5538},[5465,7835,7666],{"class":5503},[5465,7837,7712],{"class":5503},[5465,7839,7840],{"class":5467,"line":5507},[5465,7841,5529],{"emptyLinePlaceholder":5528},[5465,7843,7844],{"class":5467,"line":5519},[5465,7845,7846],{"class":5471},"# Завантаження бінарного protoc з офіційного релізу\n",[5465,7848,7849],{"class":5467,"line":5525},[5465,7850,7851],{"class":5471},"# https:\u002F\u002Fgithub.com\u002Fprotocolbuffers\u002Fprotobuf\u002Freleases\n",[5280,7853,7854],{},[5232,7855,7856],{},"Структура проєкту з gRPC:",[5456,7858,7863],{"className":7859,"code":7861,"language":7862},[7860],"language-text","userservice\u002F\n├── proto\u002F\n│   └── user.proto          # Опис контрактів\n├── pb\u002F                     # Згенерований код (gitignore)\n│   ├── user.pb.go         # Protobuf структури\n│   └── user_grpc.pb.go    # gRPC сервіси та клієнти\n├── server\u002F\n│   └── main.go            # Реалізація gRPC-сервера\n├── client\u002F\n│   └── main.go            # Клієнтський код\n├── go.mod\n└── Makefile               # Автоматизація компіляції\n","text",[5211,7864,7861],{"__ignoreMap":5461},[5280,7866,7867],{},[5232,7868,7869],{},"Команда компіляції .proto файлу:",[5456,7871,7873],{"className":7649,"code":7872,"language":7651,"meta":5461,"style":5461},"protoc \\\n  --go_out=. \\\n  --go_opt=paths=source_relative \\\n  --go-grpc_out=. \\\n  --go-grpc_opt=paths=source_relative \\\n  proto\u002Fuser.proto\n",[5211,7874,7875,7883,7890,7897,7904,7911],{"__ignoreMap":5461},[5465,7876,7877,7879],{"class":5467,"line":5468},[5465,7878,6435],{"class":5538},[5465,7880,7882],{"class":7881},"sjcCO"," \\\n",[5465,7884,7885,7888],{"class":5467,"line":5475},[5465,7886,7887],{"class":5478},"  --go_out=.",[5465,7889,7882],{"class":7881},[5465,7891,7892,7895],{"class":5467,"line":5493},[5465,7893,7894],{"class":5478},"  --go_opt=paths=source_relative",[5465,7896,7882],{"class":7881},[5465,7898,7899,7902],{"class":5467,"line":5507},[5465,7900,7901],{"class":5478},"  --go-grpc_out=.",[5465,7903,7882],{"class":7881},[5465,7905,7906,7909],{"class":5467,"line":5519},[5465,7907,7908],{"class":5478},"  --go-grpc_opt=paths=source_relative",[5465,7910,7882],{"class":7881},[5465,7912,7913],{"class":5467,"line":5525},[5465,7914,7915],{"class":5503},"  proto\u002Fuser.proto\n",[5280,7917,7918],{},[5232,7919,7920],{},"Пояснення параметрів:",[5198,7922,7923,7932,7941,7950,7959],{},[5201,7924,7925,7928,7929,6446],{},[5211,7926,7927],{},"--go_out=."," — директорія для згенерованих Protobuf-структур (файли ",[5211,7930,7931],{},"*.pb.go",[5201,7933,7934,7937,7938,7940],{},[5211,7935,7936],{},"--go_opt=paths=source_relative"," — генерувати файли відносно розташування ",[5211,7939,5213],{}," файлів (зберігає структуру каталогів).",[5201,7942,7943,7946,7947,6446],{},[5211,7944,7945],{},"--go-grpc_out=."," — директорія для згенерованих gRPC-інтерфейсів та клієнтів (файли ",[5211,7948,7949],{},"*_grpc.pb.go",[5201,7951,7952,7955,7956,5214],{},[5211,7953,7954],{},"--go-grpc_opt=paths=source_relative"," — аналогічно до ",[5211,7957,7958],{},"--go_opt",[5201,7960,7961,7964],{},[5211,7962,7963],{},"proto\u002Fuser.proto"," — вхідний файл контракту.",[5280,7966,7967],{},[5232,7968,7969],{},"Автоматизація через Makefile:",[5456,7971,7975],{"className":7972,"code":7973,"language":7974,"meta":5461,"style":5461},"language-makefile shiki shiki-themes light-plus dark-plus dark-plus",".PHONY: proto clean\n\nproto:\n    @echo \"Generating Protocol Buffers code...\"\n    protoc \\\n        --go_out=. \\\n        --go_opt=paths=source_relative \\\n        --go-grpc_out=. \\\n        --go-grpc_opt=paths=source_relative \\\n        proto\u002F*.proto\n    @echo \"Code generation completed.\"\n\nclean:\n    @echo \"Cleaning generated files...\"\n    rm -f pb\u002F*.pb.go\n","makefile",[5211,7976,7977,7982,7986,7991,7996,8001,8006,8011,8016,8021,8026,8031,8035,8040,8045],{"__ignoreMap":5461},[5465,7978,7979],{"class":5467,"line":5468},[5465,7980,7981],{},".PHONY: proto clean\n",[5465,7983,7984],{"class":5467,"line":5475},[5465,7985,5529],{"emptyLinePlaceholder":5528},[5465,7987,7988],{"class":5467,"line":5493},[5465,7989,7990],{},"proto:\n",[5465,7992,7993],{"class":5467,"line":5507},[5465,7994,7995],{},"    @echo \"Generating Protocol Buffers code...\"\n",[5465,7997,7998],{"class":5467,"line":5519},[5465,7999,8000],{},"    protoc \\\n",[5465,8002,8003],{"class":5467,"line":5525},[5465,8004,8005],{},"        --go_out=. \\\n",[5465,8007,8008],{"class":5467,"line":5532},[5465,8009,8010],{},"        --go_opt=paths=source_relative \\\n",[5465,8012,8013],{"class":5467,"line":5565},[5465,8014,8015],{},"        --go-grpc_out=. \\\n",[5465,8017,8018],{"class":5467,"line":5594},[5465,8019,8020],{},"        --go-grpc_opt=paths=source_relative \\\n",[5465,8022,8023],{"class":5467,"line":5600},[5465,8024,8025],{},"        proto\u002F*.proto\n",[5465,8027,8028],{"class":5467,"line":5624},[5465,8029,8030],{},"    @echo \"Code generation completed.\"\n",[5465,8032,8033],{"class":5467,"line":5639},[5465,8034,5529],{"emptyLinePlaceholder":5528},[5465,8036,8037],{"class":5467,"line":5647},[5465,8038,8039],{},"clean:\n",[5465,8041,8042],{"class":5467,"line":5666},[5465,8043,8044],{},"    @echo \"Cleaning generated files...\"\n",[5465,8046,8047],{"class":5467,"line":5672},[5465,8048,8049],{},"    rm -f pb\u002F*.pb.go\n",[5280,8051,8052,8053,8056],{},"Після виконання команди ",[5211,8054,8055],{},"make proto"," будуть згенеровані два файли:",[5280,8058,8059,8065],{},[5232,8060,8061,8062],{},"1. ",[5211,8063,8064],{},"pb\u002Fuser.pb.go"," — містить структури Go для всіх повідомлень, enum-типів та методи серіалізації\u002Fдесеріалізації.",[5280,8067,8068,8074],{},[5232,8069,8070,8071],{},"2. ",[5211,8072,8073],{},"pb\u002Fuser_grpc.pb.go"," — містить інтерфейси серверних сервісів, клієнтські стаби та допоміжні функції для реєстрації обробників.",[6839,8076,8077,8078,8081,8082,8085,8086,8088],{},"Додайте згенеровані файли ",[5211,8079,8080],{},"pb\u002F*.pb.go"," до ",[5211,8083,8084],{},".gitignore"," та зберігайте лише ",[5211,8087,5213],{}," файли у системі контролю версій. Кожен розробник та CI\u002FCD пайплайн повинні регенерувати код локально. Це гарантує, що згенерований код завжди відповідає версії компілятора та плагінів, що використовуються у проєкті.",[5267,8090],{},[5270,8092,8094],{"id":8093},"транспортний-рівень-grpc-http2-та-його-переваги","Транспортний рівень gRPC: HTTP\u002F2 та його переваги",[5275,8096,8098],{"id":8097},"фундаментальні-обмеження-http11","Фундаментальні обмеження HTTP\u002F1.1",[5280,8100,8101],{},"Щоб зрозуміти революційність HTTP\u002F2 у контексті gRPC, необхідно чітко усвідомлювати фундаментальні обмеження HTTP\u002F1.1, що роблять його неефективним для інтенсивної міжсервісної комунікації.",[5280,8103,8104],{},[5232,8105,8106],{},"Проблема 1: Head-of-Line Blocking (блокування черги запитів).",[5280,8108,8109,8110,8113,8114,8117],{},"HTTP\u002F1.1 дозволяє лише один активний запит на одне TCP-з'єднання у будь-який момент часу. Навіть якщо використовується режим ",[5211,8111,8112],{},"Connection: keep-alive",", що дозволяє повторно використовувати з'єднання для кількох послідовних запитів, ці запити виконуються ",[5232,8115,8116],{},"строго послідовно",". Клієнт повинен чекати на завершення відповіді на перший запит перед надсиланням другого.",[5280,8119,8120],{},"Якщо перший запит обробляється повільно (наприклад, виконання складного запиту до бази даних займає 500 мс), усі наступні запити чекають у черзі, навіть якщо вони могли б бути виконані миттєво. Для паралельного виконання кількох запитів клієнт повинен відкрити кілька TCP-з'єднань, що призводить до збільшення споживання пам'яті, дескрипторів файлів та повільного старту TCP (TCP Slow Start).",[5280,8122,8123],{},[5232,8124,8125],{},"Проблема 2: Надмірні накладні витрати на заголовки.",[5280,8127,8128],{},"Кожен HTTP\u002F1.1 запит містить текстові заголовки, які повторюються практично без змін:",[5456,8130,8133],{"className":8131,"code":8132,"language":5613,"meta":5461,"style":5461},"language-http shiki shiki-themes light-plus dark-plus dark-plus","GET \u002Fapi\u002Fv1\u002Fusers\u002F42 HTTP\u002F1.1\nHost: api.example.com\nUser-Agent: Go-http-client\u002F1.1\nAccept: application\u002Fjson\nAuthorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\nAccept-Encoding: gzip, deflate\nConnection: keep-alive\n",[5211,8134,8135,8151,8162,8171,8180,8189,8199],{"__ignoreMap":5461},[5465,8136,8137,8140,8143,8146,8148],{"class":5467,"line":5468},[5465,8138,8139],{"class":5675},"GET",[5465,8141,8142],{"class":5489}," \u002Fapi\u002Fv1\u002Fusers\u002F42 ",[5465,8144,8145],{"class":5478},"HTTP",[5465,8147,7503],{"class":5489},[5465,8149,8150],{"class":7522},"1.1\n",[5465,8152,8153,8156,8159],{"class":5467,"line":5475},[5465,8154,5361],{"class":8155},"sKtos",[5465,8157,8158],{"class":5478},":",[5465,8160,8161],{"class":5503}," api.example.com\n",[5465,8163,8164,8166,8168],{"class":5467,"line":5493},[5465,8165,5364],{"class":8155},[5465,8167,8158],{"class":5478},[5465,8169,8170],{"class":5503}," Go-http-client\u002F1.1\n",[5465,8172,8173,8175,8177],{"class":5467,"line":5507},[5465,8174,5367],{"class":8155},[5465,8176,8158],{"class":5478},[5465,8178,8179],{"class":5503}," application\u002Fjson\n",[5465,8181,8182,8184,8186],{"class":5467,"line":5519},[5465,8183,5370],{"class":8155},[5465,8185,8158],{"class":5478},[5465,8187,8188],{"class":5503}," Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\n",[5465,8190,8191,8194,8196],{"class":5467,"line":5525},[5465,8192,8193],{"class":8155},"Accept-Encoding",[5465,8195,8158],{"class":5478},[5465,8197,8198],{"class":5503}," gzip, deflate\n",[5465,8200,8201,8204,8206],{"class":5467,"line":5532},[5465,8202,8203],{"class":8155},"Connection",[5465,8205,8158],{"class":5478},[5465,8207,8208],{"class":5503}," keep-alive\n",[5280,8210,8211],{},"Для короткого запиту (наприклад, перевірка статусу завдання) розмір заголовків може становити 400–800 байтів, тоді як корисне навантаження відповіді — лише 50 байтів. Ефективна пропускна здатність складає лише 5–10% від переданих даних.",[5280,8213,8214],{},[5232,8215,8216],{},"Проблема 3: Відсутність пріоритизації запитів.",[5280,8218,8219],{},"У HTTP\u002F1.1 немає вбудованого механізму вказати серверу, які запити є критично важливими та повинні обробл",[5280,8221,8222],{},"ятися першими. Усі запити обробляються у порядку надходження. Якщо клієнт надіслав запит на завантаження великого файлу (низький пріоритет) одразу після запиту на отримання даних користувача (високий пріоритет), обидва запити конкурують за ресурси без диференціації важливості.",[5275,8224,8226],{"id":8225},"http2-мультиплексування-та-бінарний-протокол","HTTP\u002F2: мультиплексування та бінарний протокол",[5280,8228,8229,8230,8233,8234,8237],{},"Протокол ",[5232,8231,8232],{},"HTTP\u002F2"," (RFC 7540, затверджений у 2015 році) був розроблений для вирішення всіх перелічених проблем HTTP\u002F1.1. HTTP\u002F2 є бінарним протоколом, що працює поверх одного TCP-з'єднання та забезпечує ",[5232,8235,8236],{},"повне мультиплексування"," запитів без блокування.",[5280,8239,8240],{},[5232,8241,8242],{},"Ключові інновації HTTP\u002F2:",[5280,8244,8245],{},[5232,8246,8247],{},"1. Мультиплексування потоків (Stream Multiplexing).",[5280,8249,8250,8251,5321,8254,8257,8258,5321,8261,8264],{},"HTTP\u002F2 розбиває кожен запит та відповідь на послідовність ",[5232,8252,8253],{},"фреймів",[5284,8255,8256],{},"frames","), що передаються через логічні ",[5232,8259,8260],{},"потоки",[5284,8262,8263],{},"streams","). Кожен потік має унікальний ідентифікатор, і фрейми різних потоків можуть передаватися паралельно через одне TCP-з'єднання.",[5280,8266,8267],{},"Це означає, що клієнт може надіслати 100 запитів одночасно через одне з'єднання, і сервер може обробляти їх паралельно, надсилаючи відповіді по мірі готовності. Повільний запит не блокує швидкі запити — вони виконуються незалежно.",[5280,8269,8270],{},[5232,8271,8272],{},"2. Бінарне кодування фреймів.",[5280,8274,8275],{},"На відміну від текстових заголовків HTTP\u002F1.1, HTTP\u002F2 використовує компактне бінарне представлення метаданих. Кожен фрейм має фіксований заголовок довжиною 9 байтів:",[5456,8277,8280],{"className":8278,"code":8279,"language":7862},[7860],"+-----------------------------------------------+\n|                 Length (24 біти)              |\n+---------------+---------------+---------------+\n|   Type (8)    |   Flags (8)   |\n+-+-------------+---------------+-------------------------------+\n|R|                 Stream Identifier (31 біт)                  |\n+=+=============================================================+\n|                   Frame Payload (0...16384 байтів)           |\n+---------------------------------------------------------------+\n",[5211,8281,8279],{"__ignoreMap":5461},[5280,8283,8284,8285,8288,8289,8292,8293,8296,8297,8300,8301,8304,8305,8308,8309,8312],{},"Типи фреймів включають ",[5211,8286,8287],{},"DATA"," (корисне навантаження), ",[5211,8290,8291],{},"HEADERS"," (заголовки), ",[5211,8294,8295],{},"PRIORITY"," (пріоритет потоку), ",[5211,8298,8299],{},"RST_STREAM"," (скасування потоку), ",[5211,8302,8303],{},"SETTINGS"," (налаштування з'єднання), ",[5211,8306,8307],{},"PING"," (перевірка живості), ",[5211,8310,8311],{},"GOAWAY"," (плавне закриття з'єднання).",[5280,8314,8315],{},[5232,8316,8317],{},"3. Стиснення заголовків HPACK.",[5280,8319,8320,8321,8324],{},"HTTP\u002F2 використовує алгоритм ",[5232,8322,8323],{},"HPACK"," (RFC 7541) для стиснення заголовків із збереженням стану. HPACK будує динамічну таблицю повторюваних заголовків та передає лише індекси замість повних значень. Для типового REST API це зменшує розмір заголовків на 85–95%.",[5280,8326,8327,8328,8331,8332,8335],{},"Приклад: перший запит надсилає повний заголовок ",[5211,8329,8330],{},"Authorization: Bearer \u003Ctoken>"," (200 байтів). Наступні запити надсилають лише індекс ",[5211,8333,8334],{},"62",", що посилається на попереднє значення (1 байт замість 200).",[5280,8337,8338],{},[5232,8339,8340],{},"4. Пріоритизація потоків (Stream Prioritization).",[5280,8342,8343,8344,8347,8348,8351],{},"HTTP\u002F2 дозволяє клієнту вказати пріоритет кожного потоку через дерево залежностей (",[5284,8345,8346],{},"dependency tree",") та вагові коефіцієнти (",[5284,8349,8350],{},"weight","). Сервер може використовувати цю інформацію для оптимального розподілу пропускної здатності між потоками.",[5280,8353,8354],{},[5232,8355,8356],{},"5. Server Push (серверна ініціатива).",[5280,8358,8359],{},"Механізм, що дозволяє серверу надсилати ресурси клієнту до того, як клієнт їх запитав. Наприклад, при запиті HTML-сторінки сервер може одразу надіслати CSS та JavaScript файли. Ця функція рідко використовується у gRPC, але є доступною для спеціальних сценаріїв.",[6076,8361,8363],{"alt":8362},"Порівняння HTTP\u002F1.1 vs HTTP\u002F2: паралельні запити",[5456,8364,8366],{"className":6081,"code":8365,"language":6083,"meta":5461,"style":5461},"@startuml\nskinparam style plain\nskinparam backgroundColor #FFFFFF\n\nrectangle \"HTTP\u002F1.1: Послідовна обробка\" #FECACA {\n  collections \"Клієнт\" as C1\n  database \"Сервер\" as S1\n  \n  C1 -[#FF6B6B]> S1 : Request 1 (350 ms)\n  S1 -[#FF6B6B]> C1 : Response 1\n  \n  C1 -[#FFA94D]> S1 : Request 2 (50 ms) - чекає!\n  S1 -[#FFA94D]> C1 : Response 2\n  \n  C1 -[#4ECDC4]> S1 : Request 3 (100 ms) - чекає!\n  S1 -[#4ECDC4]> C1 : Response 3\n  \n  note bottom of C1 #FEF3C7\n    Загальний час: 350 + 50 + 100 = **500 мс**\n    (послідовне виконання)\n  end note\n}\n\nrectangle \"HTTP\u002F2: Мультиплексування\" #DCFCE7 {\n  collections \"Клієнт\" as C2\n  database \"Сервер\" as S2\n  \n  C2 -[#FF6B6B]> S2 : Stream 1 (350 ms)\n  C2 -[#FFA94D]> S2 : Stream 2 (50 ms)\n  C2 -[#4ECDC4]> S2 : Stream 3 (100 ms)\n  \n  S2 -[#FFA94D]> C2 : Response Stream 2 (50 ms)\n  S2 -[#4ECDC4]> C2 : Response Stream 3 (100 ms)\n  S2 -[#FF6B6B]> C2 : Response Stream 1 (350 ms)\n  \n  note bottom of C2 #D4F4DD\n    Загальний час: max(350, 50, 100) = **350 мс**\n    (паралельне виконання через один TCP-з'єднання)\n  end note\n}\n\n@enduml\n",[5211,8367,8368,8372,8376,8380,8384,8389,8394,8399,8403,8408,8413,8417,8422,8427,8431,8436,8441,8445,8450,8455,8460,8465,8469,8473,8478,8483,8488,8492,8497,8502,8507,8511,8516,8521,8526,8530,8535,8540,8545,8549,8553,8557],{"__ignoreMap":5461},[5465,8369,8370],{"class":5467,"line":5468},[5465,8371,6090],{},[5465,8373,8374],{"class":5467,"line":5475},[5465,8375,6095],{},[5465,8377,8378],{"class":5467,"line":5493},[5465,8379,6100],{},[5465,8381,8382],{"class":5467,"line":5507},[5465,8383,5529],{"emptyLinePlaceholder":5528},[5465,8385,8386],{"class":5467,"line":5519},[5465,8387,8388],{},"rectangle \"HTTP\u002F1.1: Послідовна обробка\" #FECACA {\n",[5465,8390,8391],{"class":5467,"line":5525},[5465,8392,8393],{},"  collections \"Клієнт\" as C1\n",[5465,8395,8396],{"class":5467,"line":5532},[5465,8397,8398],{},"  database \"Сервер\" as S1\n",[5465,8400,8401],{"class":5467,"line":5565},[5465,8402,7201],{},[5465,8404,8405],{"class":5467,"line":5594},[5465,8406,8407],{},"  C1 -[#FF6B6B]> S1 : Request 1 (350 ms)\n",[5465,8409,8410],{"class":5467,"line":5600},[5465,8411,8412],{},"  S1 -[#FF6B6B]> C1 : Response 1\n",[5465,8414,8415],{"class":5467,"line":5624},[5465,8416,7201],{},[5465,8418,8419],{"class":5467,"line":5639},[5465,8420,8421],{},"  C1 -[#FFA94D]> S1 : Request 2 (50 ms) - чекає!\n",[5465,8423,8424],{"class":5467,"line":5647},[5465,8425,8426],{},"  S1 -[#FFA94D]> C1 : Response 2\n",[5465,8428,8429],{"class":5467,"line":5666},[5465,8430,7201],{},[5465,8432,8433],{"class":5467,"line":5672},[5465,8434,8435],{},"  C1 -[#4ECDC4]> S1 : Request 3 (100 ms) - чекає!\n",[5465,8437,8438],{"class":5467,"line":5690},[5465,8439,8440],{},"  S1 -[#4ECDC4]> C1 : Response 3\n",[5465,8442,8443],{"class":5467,"line":5699},[5465,8444,7201],{},[5465,8446,8447],{"class":5467,"line":5705},[5465,8448,8449],{},"  note bottom of C1 #FEF3C7\n",[5465,8451,8452],{"class":5467,"line":5727},[5465,8453,8454],{},"    Загальний час: 350 + 50 + 100 = **500 мс**\n",[5465,8456,8457],{"class":5467,"line":5732},[5465,8458,8459],{},"    (послідовне виконання)\n",[5465,8461,8462],{"class":5467,"line":5755},[5465,8463,8464],{},"  end note\n",[5465,8466,8467],{"class":5467,"line":5786},[5465,8468,5522],{},[5465,8470,8471],{"class":5467,"line":5831},[5465,8472,5529],{"emptyLinePlaceholder":5528},[5465,8474,8475],{"class":5467,"line":5836},[5465,8476,8477],{},"rectangle \"HTTP\u002F2: Мультиплексування\" #DCFCE7 {\n",[5465,8479,8480],{"class":5467,"line":5841},[5465,8481,8482],{},"  collections \"Клієнт\" as C2\n",[5465,8484,8485],{"class":5467,"line":5850},[5465,8486,8487],{},"  database \"Сервер\" as S2\n",[5465,8489,8490],{"class":5467,"line":6213},[5465,8491,7201],{},[5465,8493,8494],{"class":5467,"line":6218},[5465,8495,8496],{},"  C2 -[#FF6B6B]> S2 : Stream 1 (350 ms)\n",[5465,8498,8499],{"class":5467,"line":6224},[5465,8500,8501],{},"  C2 -[#FFA94D]> S2 : Stream 2 (50 ms)\n",[5465,8503,8504],{"class":5467,"line":6230},[5465,8505,8506],{},"  C2 -[#4ECDC4]> S2 : Stream 3 (100 ms)\n",[5465,8508,8509],{"class":5467,"line":6236},[5465,8510,7201],{},[5465,8512,8513],{"class":5467,"line":6242},[5465,8514,8515],{},"  S2 -[#FFA94D]> C2 : Response Stream 2 (50 ms)\n",[5465,8517,8518],{"class":5467,"line":6248},[5465,8519,8520],{},"  S2 -[#4ECDC4]> C2 : Response Stream 3 (100 ms)\n",[5465,8522,8523],{"class":5467,"line":6254},[5465,8524,8525],{},"  S2 -[#FF6B6B]> C2 : Response Stream 1 (350 ms)\n",[5465,8527,8528],{"class":5467,"line":6260},[5465,8529,7201],{},[5465,8531,8532],{"class":5467,"line":6265},[5465,8533,8534],{},"  note bottom of C2 #D4F4DD\n",[5465,8536,8537],{"class":5467,"line":6270},[5465,8538,8539],{},"    Загальний час: max(350, 50, 100) = **350 мс**\n",[5465,8541,8542],{"class":5467,"line":6276},[5465,8543,8544],{},"    (паралельне виконання через один TCP-з'єднання)\n",[5465,8546,8547],{"class":5467,"line":6282},[5465,8548,8464],{},[5465,8550,8551],{"class":5467,"line":6288},[5465,8552,5522],{},[5465,8554,8555],{"class":5467,"line":6294},[5465,8556,5529],{"emptyLinePlaceholder":5528},[5465,8558,8559],{"class":5467,"line":6300},[5465,8560,6377],{},[5275,8562,8564],{"id":8563},"чому-grpc-обрав-http2","Чому gRPC обрав HTTP\u002F2",[5280,8566,8567],{},"Вибір HTTP\u002F2 як транспортного протоколу для gRPC не був випадковим. HTTP\u002F2 надає всі необхідні примітиви для високопродуктивної RPC-комунікації:",[5280,8569,8570],{},[5232,8571,8572],{},"Переваг 1: Ефективність мережевого обміну.",[5280,8574,8575],{},"Мультиплексування дозволяє gRPC виконувати тисячі паралельних викликів через одне з'єднання без витрат на встановлення нових TCP-сокетів. Стиснення заголовків HPACK зменшує накладні витрати на метадані до 1–2% від загального трафіку.",[5280,8577,8578],{},[5232,8579,8580],{},"Перевага 2: Нативна підтримка потокових операцій.",[5280,8582,8583,8584,5321,8587,8590],{},"HTTP\u002F2 потоки ідеально підходять для реалізації ",[5232,8585,8586],{},"потокових RPC",[5284,8588,8589],{},"streaming RPC","), де клієнт або сервер надсилають послідовність повідомлень у рамках одного виклику. Це критично важливо для сценаріїв типу:",[5198,8592,8593,8596,8599],{},[5201,8594,8595],{},"Завантаження великих списків даних частинами.",[5201,8597,8598],{},"Потокова обробка файлів або відео.",[5201,8600,8601],{},"Двосторонній обмін повідомленнями у реальному часі (чати, телеметрія).",[5280,8603,8604],{},[5232,8605,8606],{},"Перевага 3: Стандартизація та сумісність з інфраструктурою.",[5280,8608,8609],{},"HTTP\u002F2 є стандартом IETF (RFC 7540), що означає широку підтримку у проксі-серверах, балансувальниках навантаження (Nginx, HAProxy, Envoy), service mesh системах (Istio, Linkerd) та хмарних платформах. gRPC-трафік може проходити через існуючу HTTP-інфраструктуру без спеціальних налаштувань.",[5280,8611,8612],{},[5232,8613,8614],{},"Перевага 4: Підтримка TLS та безпеки.",[5280,8616,8617],{},"HTTP\u002F2 має вбудовану підтримку TLS (хоча формально може працювати і без шифрування, більшість реалізацій вимагають TLS). gRPC успадковує всі механізми безпеки HTTP\u002F2: шифрування каналу, автентифікацію сертифікатів, mutual TLS (mTLS).",[7043,8619,8620,8621,8624],{},"Попри те, що HTTP\u002F2 формально підтримує незашифрований режим (",[5284,8622,8623],{},"h2c — HTTP\u002F2 cleartext","), більшість вебсерверів та браузерів вимагають TLS для HTTP\u002F2. У gRPC рекомендується завжди використовувати TLS у продакшн-середовищі для захисту від прослуховування та атак Man-in-the-Middle. Незашифрований gRPC слід використовувати лише для локальної розробки або комунікації всередині захищеної мережі (наприклад, між подами у Kubernetes кластері з mutual TLS через service mesh).",[5267,8626],{},[5270,8628,8630],{"id":8629},"визначення-grpc-сервісів-у-protocol-buffers","Визначення gRPC-сервісів у Protocol Buffers",[5275,8632,8634],{"id":8633},"синтаксис-опису-rpc-методів","Синтаксис опису RPC-методів",[5280,8636,8637,8638,8641,8642,5214],{},"Після визначення структур даних (повідомлень) необхідно описати ",[5232,8639,8640],{},"інтерфейс сервісу"," — набір методів, які клієнт може викликати віддалено. У Protocol Buffers це робиться за допомогою ключового слова ",[5211,8643,8644],{},"service",[5280,8646,8647,8648,8651],{},"Розширимо попередній приклад ",[5211,8649,8650],{},"user.proto",", додавши повноцінний gRPC-сервіс управління користувачами:",[5456,8653,8655],{"className":6471,"code":8654,"language":6473,"meta":5461,"style":5461},"syntax = \"proto3\";\n\npackage userservice;\n\noption go_package = \"github.com\u002Fexample\u002Fuserservice\u002Fpb\";\n\n\u002F\u002F ... (повідомлення User, UserStatus, CreateUserRequest тощо)\n\n\u002F\u002F Сервіс управління користувачами\nservice UserService {\n  \u002F\u002F Unary RPC: створення нового користувача\n  rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);\n  \n  \u002F\u002F Unary RPC: отримання користувача за ідентифікатором\n  rpc GetUser(GetUserRequest) returns (GetUserResponse);\n  \n  \u002F\u002F Unary RPC: оновлення даних користувача\n  rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse);\n  \n  \u002F\u002F Unary RPC: видалення користувача\n  rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse);\n  \n  \u002F\u002F Server Streaming RPC: отримання списку всіх користувачів потоком\n  rpc ListUsers(ListUsersRequest) returns (stream User);\n  \n  \u002F\u002F Client Streaming RPC: пакетне створення користувачів\n  rpc BatchCreateUsers(stream CreateUserRequest) returns (BatchCreateUsersResponse);\n  \n  \u002F\u002F Bidirectional Streaming RPC: двосторонній чат підтримки\n  rpc SupportChat(stream ChatMessage) returns (stream ChatMessage);\n}\n\n\u002F\u002F Додаткові повідомлення для нових методів\n\nmessage UpdateUserRequest {\n  int64 id = 1;\n  string name = 2;\n  string email = 3;\n}\n\nmessage UpdateUserResponse {\n  User user = 1;\n}\n\nmessage DeleteUserRequest {\n  int64 id = 1;\n}\n\nmessage DeleteUserResponse {\n  bool success = 1;\n}\n\nmessage ListUsersRequest {\n  int32 page_size = 1;\n  string filter = 2;\n}\n\nmessage BatchCreateUsersResponse {\n  int32 created_count = 1;\n  repeated User users = 2;\n}\n\nmessage ChatMessage {\n  int64 user_id = 1;\n  string message = 2;\n  int64 timestamp = 3;\n}\n",[5211,8656,8657,8661,8665,8669,8673,8677,8681,8686,8690,8695,8700,8705,8710,8714,8719,8724,8728,8733,8738,8742,8747,8752,8756,8761,8766,8770,8775,8780,8784,8789,8794,8798,8802,8807,8811,8816,8820,8824,8828,8832,8836,8841,8845,8849,8853,8858,8862,8866,8870,8875,8880,8884,8888,8893,8898,8903,8908,8913,8919,8925,8931,8936,8941,8947,8953,8959,8965],{"__ignoreMap":5461},[5465,8658,8659],{"class":5467,"line":5468},[5465,8660,6485],{},[5465,8662,8663],{"class":5467,"line":5475},[5465,8664,5529],{"emptyLinePlaceholder":5528},[5465,8666,8667],{"class":5467,"line":5493},[5465,8668,6494],{},[5465,8670,8671],{"class":5467,"line":5507},[5465,8672,5529],{"emptyLinePlaceholder":5528},[5465,8674,8675],{"class":5467,"line":5519},[5465,8676,6503],{},[5465,8678,8679],{"class":5467,"line":5525},[5465,8680,5529],{"emptyLinePlaceholder":5528},[5465,8682,8683],{"class":5467,"line":5532},[5465,8684,8685],{},"\u002F\u002F ... (повідомлення User, UserStatus, CreateUserRequest тощо)\n",[5465,8687,8688],{"class":5467,"line":5565},[5465,8689,5529],{"emptyLinePlaceholder":5528},[5465,8691,8692],{"class":5467,"line":5594},[5465,8693,8694],{},"\u002F\u002F Сервіс управління користувачами\n",[5465,8696,8697],{"class":5467,"line":5600},[5465,8698,8699],{},"service UserService {\n",[5465,8701,8702],{"class":5467,"line":5624},[5465,8703,8704],{},"  \u002F\u002F Unary RPC: створення нового користувача\n",[5465,8706,8707],{"class":5467,"line":5639},[5465,8708,8709],{},"  rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);\n",[5465,8711,8712],{"class":5467,"line":5647},[5465,8713,7201],{},[5465,8715,8716],{"class":5467,"line":5666},[5465,8717,8718],{},"  \u002F\u002F Unary RPC: отримання користувача за ідентифікатором\n",[5465,8720,8721],{"class":5467,"line":5672},[5465,8722,8723],{},"  rpc GetUser(GetUserRequest) returns (GetUserResponse);\n",[5465,8725,8726],{"class":5467,"line":5690},[5465,8727,7201],{},[5465,8729,8730],{"class":5467,"line":5699},[5465,8731,8732],{},"  \u002F\u002F Unary RPC: оновлення даних користувача\n",[5465,8734,8735],{"class":5467,"line":5705},[5465,8736,8737],{},"  rpc UpdateUser(UpdateUserRequest) returns (UpdateUserResponse);\n",[5465,8739,8740],{"class":5467,"line":5727},[5465,8741,7201],{},[5465,8743,8744],{"class":5467,"line":5732},[5465,8745,8746],{},"  \u002F\u002F Unary RPC: видалення користувача\n",[5465,8748,8749],{"class":5467,"line":5755},[5465,8750,8751],{},"  rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse);\n",[5465,8753,8754],{"class":5467,"line":5786},[5465,8755,7201],{},[5465,8757,8758],{"class":5467,"line":5831},[5465,8759,8760],{},"  \u002F\u002F Server Streaming RPC: отримання списку всіх користувачів потоком\n",[5465,8762,8763],{"class":5467,"line":5836},[5465,8764,8765],{},"  rpc ListUsers(ListUsersRequest) returns (stream User);\n",[5465,8767,8768],{"class":5467,"line":5841},[5465,8769,7201],{},[5465,8771,8772],{"class":5467,"line":5850},[5465,8773,8774],{},"  \u002F\u002F Client Streaming RPC: пакетне створення користувачів\n",[5465,8776,8777],{"class":5467,"line":6213},[5465,8778,8779],{},"  rpc BatchCreateUsers(stream CreateUserRequest) returns (BatchCreateUsersResponse);\n",[5465,8781,8782],{"class":5467,"line":6218},[5465,8783,7201],{},[5465,8785,8786],{"class":5467,"line":6224},[5465,8787,8788],{},"  \u002F\u002F Bidirectional Streaming RPC: двосторонній чат підтримки\n",[5465,8790,8791],{"class":5467,"line":6230},[5465,8792,8793],{},"  rpc SupportChat(stream ChatMessage) returns (stream ChatMessage);\n",[5465,8795,8796],{"class":5467,"line":6236},[5465,8797,5522],{},[5465,8799,8800],{"class":5467,"line":6242},[5465,8801,5529],{"emptyLinePlaceholder":5528},[5465,8803,8804],{"class":5467,"line":6248},[5465,8805,8806],{},"\u002F\u002F Додаткові повідомлення для нових методів\n",[5465,8808,8809],{"class":5467,"line":6254},[5465,8810,5529],{"emptyLinePlaceholder":5528},[5465,8812,8813],{"class":5467,"line":6260},[5465,8814,8815],{},"message UpdateUserRequest {\n",[5465,8817,8818],{"class":5467,"line":6265},[5465,8819,6527],{},[5465,8821,8822],{"class":5467,"line":6270},[5465,8823,6532],{},[5465,8825,8826],{"class":5467,"line":6276},[5465,8827,6537],{},[5465,8829,8830],{"class":5467,"line":6282},[5465,8831,5522],{},[5465,8833,8834],{"class":5467,"line":6288},[5465,8835,5529],{"emptyLinePlaceholder":5528},[5465,8837,8838],{"class":5467,"line":6294},[5465,8839,8840],{},"message UpdateUserResponse {\n",[5465,8842,8843],{"class":5467,"line":6300},[5465,8844,6636],{},[5465,8846,8847],{"class":5467,"line":6306},[5465,8848,5522],{},[5465,8850,8851],{"class":5467,"line":6311},[5465,8852,5529],{"emptyLinePlaceholder":5528},[5465,8854,8855],{"class":5467,"line":6316},[5465,8856,8857],{},"message DeleteUserRequest {\n",[5465,8859,8860],{"class":5467,"line":6322},[5465,8861,6527],{},[5465,8863,8864],{"class":5467,"line":6328},[5465,8865,5522],{},[5465,8867,8868],{"class":5467,"line":6334},[5465,8869,5529],{"emptyLinePlaceholder":5528},[5465,8871,8872],{"class":5467,"line":6340},[5465,8873,8874],{},"message DeleteUserResponse {\n",[5465,8876,8877],{"class":5467,"line":6346},[5465,8878,8879],{},"  bool success = 1;\n",[5465,8881,8882],{"class":5467,"line":6352},[5465,8883,5522],{},[5465,8885,8886],{"class":5467,"line":6358},[5465,8887,5529],{"emptyLinePlaceholder":5528},[5465,8889,8890],{"class":5467,"line":6364},[5465,8891,8892],{},"message ListUsersRequest {\n",[5465,8894,8895],{"class":5467,"line":6369},[5465,8896,8897],{},"  int32 page_size = 1;\n",[5465,8899,8900],{"class":5467,"line":6374},[5465,8901,8902],{},"  string filter = 2;\n",[5465,8904,8906],{"class":5467,"line":8905},56,[5465,8907,5522],{},[5465,8909,8911],{"class":5467,"line":8910},57,[5465,8912,5529],{"emptyLinePlaceholder":5528},[5465,8914,8916],{"class":5467,"line":8915},58,[5465,8917,8918],{},"message BatchCreateUsersResponse {\n",[5465,8920,8922],{"class":5467,"line":8921},59,[5465,8923,8924],{},"  int32 created_count = 1;\n",[5465,8926,8928],{"class":5467,"line":8927},60,[5465,8929,8930],{},"  repeated User users = 2;\n",[5465,8932,8934],{"class":5467,"line":8933},61,[5465,8935,5522],{},[5465,8937,8939],{"class":5467,"line":8938},62,[5465,8940,5529],{"emptyLinePlaceholder":5528},[5465,8942,8944],{"class":5467,"line":8943},63,[5465,8945,8946],{},"message ChatMessage {\n",[5465,8948,8950],{"class":5467,"line":8949},64,[5465,8951,8952],{},"  int64 user_id = 1;\n",[5465,8954,8956],{"class":5467,"line":8955},65,[5465,8957,8958],{},"  string message = 2;\n",[5465,8960,8962],{"class":5467,"line":8961},66,[5465,8963,8964],{},"  int64 timestamp = 3;\n",[5465,8966,8968],{"class":5467,"line":8967},67,[5465,8969,5522],{},[5280,8971,8972],{},[5232,8973,8974,8975,8158],{},"Аналіз синтаксису ",[5211,8976,8644],{},[5280,8978,8979,8982,8983,8986],{},[5232,8980,8981],{},"Рядки 10–30:"," Визначення сервісу ",[5211,8984,8985],{},"UserService"," із семи RPC-методів. Кожен метод має:",[5198,8988,8989,9000,9010,9021],{},[5201,8990,8991,6758,8994,5336,8996,8999],{},[5232,8992,8993],{},"Ім'я методу:",[5211,8995,5912],{},[5211,8997,8998],{},"GetUser"," тощо.",[5201,9001,9002,6758,9005,5336,9008,5214],{},[5232,9003,9004],{},"Тип запиту:",[5211,9006,9007],{},"CreateUserRequest",[5211,9009,6836],{},[5201,9011,9012,6758,9015,5336,9018,5214],{},[5232,9013,9014],{},"Тип відповіді:",[5211,9016,9017],{},"CreateUserResponse",[5211,9019,9020],{},"GetUserResponse",[5201,9022,9023,9029],{},[5232,9024,9025,9026,8158],{},"Модифікатор ",[5211,9027,9028],{},"stream"," вказує на потокову передачу (може бути перед типом запиту, типом відповіді або обома).",[5280,9031,9032,9035],{},[5232,9033,9034],{},"Unary RPC (рядки 12–21):"," Найпростіший тип виклику «один запит — одна відповідь». Аналогічний до звичайного виклику функції.",[5280,9037,9038,9041],{},[5232,9039,9040],{},"Server Streaming RPC (рядок 24):"," Клієнт надсилає один запит, сервер відправляє потік повідомлень. Використовується для передачі великих списків даних, логів або прогресу виконання.",[5280,9043,9044,9047],{},[5232,9045,9046],{},"Client Streaming RPC (рядок 27):"," Клієнт надсилає потік повідомлень, сервер відправляє одну відповідь після завершення потоку. Використовується для завантаження файлів, пакетного імпорту даних.",[5280,9049,9050,9053],{},[5232,9051,9052],{},"Bidirectional Streaming RPC (рядок 30):"," Обидві сторони надсилають потоки повідомлень незалежно один від одного. Використовується для чатів, відеоконференцій, синхронізації даних у реальному часі.",[6839,9055,9056,9057,9060,9061,5336,9063,5336,9066,9069,9070,9073],{},"Віддайте перевагу створенню ",[5232,9058,9059],{},"окремих Unary RPC-методів"," для кожної операції замість «універсального» методу з перелічуваним типом дії. Наприклад, краще мати ",[5211,9062,5912],{},[5211,9064,9065],{},"UpdateUser",[5211,9067,9068],{},"DeleteUser",", ніж один метод ",[5211,9071,9072],{},"ManageUser(action: enum, data: ...)",". Це робить API явним, типобезпечним та легшим у підтримці.",[5275,9075,9077],{"id":9076},"чотири-типи-rpc-викликів-у-grpc","Чотири типи RPC-викликів у gRPC",[5280,9079,9080],{},"gRPC підтримує чотири фундаментальні патерни комунікації, кожен із яких оптимізований для специфічних сценаріїв використання.",[6076,9082,9084],{"alt":9083},"Чотири типи gRPC-викликів",[5456,9085,9087],{"className":6081,"code":9086,"language":6083,"meta":5461,"style":5461},"@startuml\nskinparam style plain\nskinparam backgroundColor #FFFFFF\nskinparam sequence {\n  ArrowColor #1E293B\n  ActorBorderColor #1E293B\n  LifeLineBorderColor #CBD5E1\n  LifeLineBackgroundColor #F8FAFC\n  \n  ParticipantBorderColor #1E293B\n  ParticipantBackgroundColor #E2E8F0\n}\n\ntitle Типи gRPC RPC-викликів\n\n== Unary RPC (Один запит → Одна відповідь) ==\n\nparticipant \"Client\" as C1 #DBEAFE\nparticipant \"Server\" as S1 #DCFCE7\n\nC1 -> S1 : CreateUser(request)\nactivate S1\nS1 --> C1 : CreateUserResponse\ndeactivate S1\n\nnote over C1, S1 #FEF3C7\n  Аналогічно до звичайного HTTP POST запиту.\n  Використання: CRUD операції, автентифікація.\nend note\n\n== Server Streaming RPC (Один запит → Потік відповідей) ==\n\nparticipant \"Client\" as C2 #DBEAFE\nparticipant \"Server\" as S2 #DCFCE7\n\nC2 -> S2 : ListUsers(request)\nactivate S2\nS2 --> C2 : User #1\nS2 --> C2 : User #2\nS2 --> C2 : User #3\nS2 --> C2 : ... (stream continues)\nS2 --> C2 : User #N\ndeactivate S2\n\nnote over C2, S2 #FEF3C7\n  Сервер надсилає послідовність повідомлень.\n  Використання: вибірка великих списків,\n  логи реального часу, метрики.\nend note\n\n== Client Streaming RPC (Потік запитів → Одна відповідь) ==\n\nparticipant \"Client\" as C3 #DBEAFE\nparticipant \"Server\" as S3 #DCFCE7\n\nactivate S3\nC3 -> S3 : CreateUserRequest #1\nC3 -> S3 : CreateUserRequest #2\nC3 -> S3 : CreateUserRequest #3\nC3 -> S3 : ... (stream continues)\nC3 -> S3 : CreateUserRequest #N\nS3 --> C3 : BatchCreateUsersResponse\ndeactivate S3\n\nnote over C3, S3 #FEF3C7\n  Клієнт надсилає потік даних,\n  сервер обробляє та повертає результат.\n  Використання: завантаження файлів,\n  пакетний імпорт, агрегація метрик.\nend note\n\n== Bidirectional Streaming RPC (Потік ↔ Потік) ==\n\nparticipant \"Client\" as C4 #DBEAFE\nparticipant \"Server\" as S4 #DCFCE7\n\nactivate S4\nactivate C4\nC4 -> S4 : ChatMessage #1\nS4 --> C4 : ChatMessage (відповідь 1)\nC4 -> S4 : ChatMessage #2\nC4 -> S4 : ChatMessage #3\nS4 --> C4 : ChatMessage (відповідь 2)\nS4 --> C4 : ChatMessage (відповідь 3)\ndeactivate C4\ndeactivate S4\n\nnote over C4, S4 #FEF3C7\n  Обидві сторони надсилають повідомлення\n  незалежно та асинхронно.\n  Використання: чати, відеоконференції,\n  онлайн-ігри, синхронізація даних.\nend note\n\n@enduml\n",[5211,9088,9089,9093,9097,9101,9106,9111,9116,9121,9126,9130,9135,9140,9144,9148,9153,9157,9162,9166,9171,9176,9180,9185,9190,9195,9200,9204,9209,9214,9219,9223,9227,9232,9236,9241,9246,9250,9255,9260,9265,9270,9275,9280,9285,9290,9294,9299,9304,9309,9314,9318,9322,9327,9331,9336,9341,9345,9350,9355,9360,9365,9370,9375,9380,9385,9389,9394,9399,9404,9410,9416,9421,9426,9432,9437,9443,9449,9454,9460,9466,9472,9478,9484,9490,9496,9502,9508,9514,9519,9525,9531,9537,9543,9549,9554,9559],{"__ignoreMap":5461},[5465,9090,9091],{"class":5467,"line":5468},[5465,9092,6090],{},[5465,9094,9095],{"class":5467,"line":5475},[5465,9096,6095],{},[5465,9098,9099],{"class":5467,"line":5493},[5465,9100,6100],{},[5465,9102,9103],{"class":5467,"line":5507},[5465,9104,9105],{},"skinparam sequence {\n",[5465,9107,9108],{"class":5467,"line":5519},[5465,9109,9110],{},"  ArrowColor #1E293B\n",[5465,9112,9113],{"class":5467,"line":5525},[5465,9114,9115],{},"  ActorBorderColor #1E293B\n",[5465,9117,9118],{"class":5467,"line":5532},[5465,9119,9120],{},"  LifeLineBorderColor #CBD5E1\n",[5465,9122,9123],{"class":5467,"line":5565},[5465,9124,9125],{},"  LifeLineBackgroundColor #F8FAFC\n",[5465,9127,9128],{"class":5467,"line":5594},[5465,9129,7201],{},[5465,9131,9132],{"class":5467,"line":5600},[5465,9133,9134],{},"  ParticipantBorderColor #1E293B\n",[5465,9136,9137],{"class":5467,"line":5624},[5465,9138,9139],{},"  ParticipantBackgroundColor #E2E8F0\n",[5465,9141,9142],{"class":5467,"line":5639},[5465,9143,5522],{},[5465,9145,9146],{"class":5467,"line":5647},[5465,9147,5529],{"emptyLinePlaceholder":5528},[5465,9149,9150],{"class":5467,"line":5666},[5465,9151,9152],{},"title Типи gRPC RPC-викликів\n",[5465,9154,9155],{"class":5467,"line":5672},[5465,9156,5529],{"emptyLinePlaceholder":5528},[5465,9158,9159],{"class":5467,"line":5690},[5465,9160,9161],{},"== Unary RPC (Один запит → Одна відповідь) ==\n",[5465,9163,9164],{"class":5467,"line":5699},[5465,9165,5529],{"emptyLinePlaceholder":5528},[5465,9167,9168],{"class":5467,"line":5705},[5465,9169,9170],{},"participant \"Client\" as C1 #DBEAFE\n",[5465,9172,9173],{"class":5467,"line":5727},[5465,9174,9175],{},"participant \"Server\" as S1 #DCFCE7\n",[5465,9177,9178],{"class":5467,"line":5732},[5465,9179,5529],{"emptyLinePlaceholder":5528},[5465,9181,9182],{"class":5467,"line":5755},[5465,9183,9184],{},"C1 -> S1 : CreateUser(request)\n",[5465,9186,9187],{"class":5467,"line":5786},[5465,9188,9189],{},"activate S1\n",[5465,9191,9192],{"class":5467,"line":5831},[5465,9193,9194],{},"S1 --> C1 : CreateUserResponse\n",[5465,9196,9197],{"class":5467,"line":5836},[5465,9198,9199],{},"deactivate S1\n",[5465,9201,9202],{"class":5467,"line":5841},[5465,9203,5529],{"emptyLinePlaceholder":5528},[5465,9205,9206],{"class":5467,"line":5850},[5465,9207,9208],{},"note over C1, S1 #FEF3C7\n",[5465,9210,9211],{"class":5467,"line":6213},[5465,9212,9213],{},"  Аналогічно до звичайного HTTP POST запиту.\n",[5465,9215,9216],{"class":5467,"line":6218},[5465,9217,9218],{},"  Використання: CRUD операції, автентифікація.\n",[5465,9220,9221],{"class":5467,"line":6224},[5465,9222,6167],{},[5465,9224,9225],{"class":5467,"line":6230},[5465,9226,5529],{"emptyLinePlaceholder":5528},[5465,9228,9229],{"class":5467,"line":6236},[5465,9230,9231],{},"== Server Streaming RPC (Один запит → Потік відповідей) ==\n",[5465,9233,9234],{"class":5467,"line":6242},[5465,9235,5529],{"emptyLinePlaceholder":5528},[5465,9237,9238],{"class":5467,"line":6248},[5465,9239,9240],{},"participant \"Client\" as C2 #DBEAFE\n",[5465,9242,9243],{"class":5467,"line":6254},[5465,9244,9245],{},"participant \"Server\" as S2 #DCFCE7\n",[5465,9247,9248],{"class":5467,"line":6260},[5465,9249,5529],{"emptyLinePlaceholder":5528},[5465,9251,9252],{"class":5467,"line":6265},[5465,9253,9254],{},"C2 -> S2 : ListUsers(request)\n",[5465,9256,9257],{"class":5467,"line":6270},[5465,9258,9259],{},"activate S2\n",[5465,9261,9262],{"class":5467,"line":6276},[5465,9263,9264],{},"S2 --> C2 : User #1\n",[5465,9266,9267],{"class":5467,"line":6282},[5465,9268,9269],{},"S2 --> C2 : User #2\n",[5465,9271,9272],{"class":5467,"line":6288},[5465,9273,9274],{},"S2 --> C2 : User #3\n",[5465,9276,9277],{"class":5467,"line":6294},[5465,9278,9279],{},"S2 --> C2 : ... (stream continues)\n",[5465,9281,9282],{"class":5467,"line":6300},[5465,9283,9284],{},"S2 --> C2 : User #N\n",[5465,9286,9287],{"class":5467,"line":6306},[5465,9288,9289],{},"deactivate S2\n",[5465,9291,9292],{"class":5467,"line":6311},[5465,9293,5529],{"emptyLinePlaceholder":5528},[5465,9295,9296],{"class":5467,"line":6316},[5465,9297,9298],{},"note over C2, S2 #FEF3C7\n",[5465,9300,9301],{"class":5467,"line":6322},[5465,9302,9303],{},"  Сервер надсилає послідовність повідомлень.\n",[5465,9305,9306],{"class":5467,"line":6328},[5465,9307,9308],{},"  Використання: вибірка великих списків,\n",[5465,9310,9311],{"class":5467,"line":6334},[5465,9312,9313],{},"  логи реального часу, метрики.\n",[5465,9315,9316],{"class":5467,"line":6340},[5465,9317,6167],{},[5465,9319,9320],{"class":5467,"line":6346},[5465,9321,5529],{"emptyLinePlaceholder":5528},[5465,9323,9324],{"class":5467,"line":6352},[5465,9325,9326],{},"== Client Streaming RPC (Потік запитів → Одна відповідь) ==\n",[5465,9328,9329],{"class":5467,"line":6358},[5465,9330,5529],{"emptyLinePlaceholder":5528},[5465,9332,9333],{"class":5467,"line":6364},[5465,9334,9335],{},"participant \"Client\" as C3 #DBEAFE\n",[5465,9337,9338],{"class":5467,"line":6369},[5465,9339,9340],{},"participant \"Server\" as S3 #DCFCE7\n",[5465,9342,9343],{"class":5467,"line":6374},[5465,9344,5529],{"emptyLinePlaceholder":5528},[5465,9346,9347],{"class":5467,"line":8905},[5465,9348,9349],{},"activate S3\n",[5465,9351,9352],{"class":5467,"line":8910},[5465,9353,9354],{},"C3 -> S3 : CreateUserRequest #1\n",[5465,9356,9357],{"class":5467,"line":8915},[5465,9358,9359],{},"C3 -> S3 : CreateUserRequest #2\n",[5465,9361,9362],{"class":5467,"line":8921},[5465,9363,9364],{},"C3 -> S3 : CreateUserRequest #3\n",[5465,9366,9367],{"class":5467,"line":8927},[5465,9368,9369],{},"C3 -> S3 : ... (stream continues)\n",[5465,9371,9372],{"class":5467,"line":8933},[5465,9373,9374],{},"C3 -> S3 : CreateUserRequest #N\n",[5465,9376,9377],{"class":5467,"line":8938},[5465,9378,9379],{},"S3 --> C3 : BatchCreateUsersResponse\n",[5465,9381,9382],{"class":5467,"line":8943},[5465,9383,9384],{},"deactivate S3\n",[5465,9386,9387],{"class":5467,"line":8949},[5465,9388,5529],{"emptyLinePlaceholder":5528},[5465,9390,9391],{"class":5467,"line":8955},[5465,9392,9393],{},"note over C3, S3 #FEF3C7\n",[5465,9395,9396],{"class":5467,"line":8961},[5465,9397,9398],{},"  Клієнт надсилає потік даних,\n",[5465,9400,9401],{"class":5467,"line":8967},[5465,9402,9403],{},"  сервер обробляє та повертає результат.\n",[5465,9405,9407],{"class":5467,"line":9406},68,[5465,9408,9409],{},"  Використання: завантаження файлів,\n",[5465,9411,9413],{"class":5467,"line":9412},69,[5465,9414,9415],{},"  пакетний імпорт, агрегація метрик.\n",[5465,9417,9419],{"class":5467,"line":9418},70,[5465,9420,6167],{},[5465,9422,9424],{"class":5467,"line":9423},71,[5465,9425,5529],{"emptyLinePlaceholder":5528},[5465,9427,9429],{"class":5467,"line":9428},72,[5465,9430,9431],{},"== Bidirectional Streaming RPC (Потік ↔ Потік) ==\n",[5465,9433,9435],{"class":5467,"line":9434},73,[5465,9436,5529],{"emptyLinePlaceholder":5528},[5465,9438,9440],{"class":5467,"line":9439},74,[5465,9441,9442],{},"participant \"Client\" as C4 #DBEAFE\n",[5465,9444,9446],{"class":5467,"line":9445},75,[5465,9447,9448],{},"participant \"Server\" as S4 #DCFCE7\n",[5465,9450,9452],{"class":5467,"line":9451},76,[5465,9453,5529],{"emptyLinePlaceholder":5528},[5465,9455,9457],{"class":5467,"line":9456},77,[5465,9458,9459],{},"activate S4\n",[5465,9461,9463],{"class":5467,"line":9462},78,[5465,9464,9465],{},"activate C4\n",[5465,9467,9469],{"class":5467,"line":9468},79,[5465,9470,9471],{},"C4 -> S4 : ChatMessage #1\n",[5465,9473,9475],{"class":5467,"line":9474},80,[5465,9476,9477],{},"S4 --> C4 : ChatMessage (відповідь 1)\n",[5465,9479,9481],{"class":5467,"line":9480},81,[5465,9482,9483],{},"C4 -> S4 : ChatMessage #2\n",[5465,9485,9487],{"class":5467,"line":9486},82,[5465,9488,9489],{},"C4 -> S4 : ChatMessage #3\n",[5465,9491,9493],{"class":5467,"line":9492},83,[5465,9494,9495],{},"S4 --> C4 : ChatMessage (відповідь 2)\n",[5465,9497,9499],{"class":5467,"line":9498},84,[5465,9500,9501],{},"S4 --> C4 : ChatMessage (відповідь 3)\n",[5465,9503,9505],{"class":5467,"line":9504},85,[5465,9506,9507],{},"deactivate C4\n",[5465,9509,9511],{"class":5467,"line":9510},86,[5465,9512,9513],{},"deactivate S4\n",[5465,9515,9517],{"class":5467,"line":9516},87,[5465,9518,5529],{"emptyLinePlaceholder":5528},[5465,9520,9522],{"class":5467,"line":9521},88,[5465,9523,9524],{},"note over C4, S4 #FEF3C7\n",[5465,9526,9528],{"class":5467,"line":9527},89,[5465,9529,9530],{},"  Обидві сторони надсилають повідомлення\n",[5465,9532,9534],{"class":5467,"line":9533},90,[5465,9535,9536],{},"  незалежно та асинхронно.\n",[5465,9538,9540],{"class":5467,"line":9539},91,[5465,9541,9542],{},"  Використання: чати, відеоконференції,\n",[5465,9544,9546],{"class":5467,"line":9545},92,[5465,9547,9548],{},"  онлайн-ігри, синхронізація даних.\n",[5465,9550,9552],{"class":5467,"line":9551},93,[5465,9553,6167],{},[5465,9555,9557],{"class":5467,"line":9556},94,[5465,9558,5529],{"emptyLinePlaceholder":5528},[5465,9560,9562],{"class":5467,"line":9561},95,[5465,9563,6377],{},[5280,9565,9566],{},[5232,9567,9568],{},"1. Unary RPC: класична модель «запит-відповідь»",[5280,9570,9571],{},"Unary RPC є найпростішим типом виклику та аналогічний до звичайного REST API запиту. Клієнт надсилає один запит та чекає на одну відповідь. Цей патерн покриває 80–90% типових операцій у системах: створення, читання, оновлення, видалення записів (CRUD), автентифікація, валідація даних.",[5280,9573,9574,9577],{},[5232,9575,9576],{},"Приклад використання:"," Реєстрація нового користувача, отримання профілю, оновлення налаштувань, виконання бізнес-транзакції.",[5280,9579,9580],{},[5232,9581,9582],{},"2. Server Streaming RPC: сервер надсилає потік даних",[5280,9584,9585],{},"Клієнт надсилає один запит, після чого сервер починає надсилати послідовність повідомлень через відкритий потік. Клієнт читає повідомлення по одному до закриття потоку сервером. Цей патерн дозволяє ефективно передавати великі обсяги даних без завантаження всього набору у пам'ять.",[5280,9587,9588],{},[5232,9589,9576],{},[5198,9591,9592,9595,9598,9601],{},[5201,9593,9594],{},"Вибірка мільйонів записів із бази даних частинами (уникнення Out-of-Memory помилок).",[5201,9596,9597],{},"Потокова передача логів системи у реальному часі.",[5201,9599,9600],{},"Передача фінансових котирувань або телеметрії IoT-пристроїв.",[5201,9602,9603],{},"Експорт звітів або резервних копій великих обсягів.",[5280,9605,9606],{},[5232,9607,9608],{},"3. Client Streaming RPC: клієнт надсилає потік даних",[5280,9610,9611],{},"Клієнт надсилає послідовність повідомлень серверу через відкритий потік. Сервер обробляє всі повідомлення та повертає одну відповідь після завершення потоку клієнтом. Цей патерн оптимізований для сценаріїв, де клієнт генерує дані динамічно або читає їх із великого джерела (файл, база даних).",[5280,9613,9614],{},[5232,9615,9576],{},[5198,9617,9618,9621,9624,9627],{},[5201,9619,9620],{},"Завантаження великих файлів на сервер частинами (resumable upload).",[5201,9622,9623],{},"Пакетний імпорт тисяч записів у базу даних.",[5201,9625,9626],{},"Збір метрик або логів із клієнтського застосунку та агрегація на сервері.",[5201,9628,9629],{},"Відправка аудіо\u002Fвідео фреймів для обробки (розпізнавання мови, аналіз відео).",[5280,9631,9632],{},[5232,9633,9634],{},"4. Bidirectional Streaming RPC: повнодуплексний обмін даними",[5280,9636,9637],{},"Обидві сторони (клієнт та сервер) відкривають потоки та надсилають повідомлення незалежно один від одного. Потоки є асинхронними — сервер може відповідати на повідомлення клієнта у довільному порядку або генерувати власні події без прив'язки до вхідних повідомлень.",[5280,9639,9640],{},[5232,9641,9576],{},[5198,9643,9644,9647,9650,9653],{},[5201,9645,9646],{},"Чати та месенджери (двосторонній обмін текстовими повідомленнями).",[5201,9648,9649],{},"Відеоконференції (обмін аудіо\u002Fвідео фреймами).",[5201,9651,9652],{},"Онлайн-ігри (синхронізація стану гри між клієнтом та сервером).",[5201,9654,9655],{},"Розподілені обчислення (клієнт надсилає завдання, сервер повертає результати по мірі готовності).",[9657,9658,9659,9721],"accordion",{},[9660,9661,9664,9667,9672,9690,9695,9715],"accordion-item",{"icon":9662,"label":9663},"i-lucide-help-circle","❓ Чому не використовувати WebSocket замість gRPC Bidirectional Streaming?",[5280,9665,9666],{},"WebSocket та gRPC Bidirectional Streaming вирішують схожі задачі двосторонньої комунікації, але мають принципово різні властивості:",[5280,9668,9669],{},[5232,9670,9671],{},"WebSocket:",[5198,9673,9674,9677,9680,9687],{},[5201,9675,9676],{},"Протокол прикладного рівня без вбудованої схеми даних. Розробник повинен самостійно проєктувати формат повідомлень (JSON, MessagePack, власний бінарний протокол).",[5201,9678,9679],{},"Немає автоматичної генерації клієнтського та серверного коду.",[5201,9681,9682,9683,9686],{},"Відсутність вбудованих механізмів таймаутів, повторних спроб (",[5284,9684,9685],{},"retries","), балансування навантаження.",[5201,9688,9689],{},"Ідеальний для вебклієнтів у браузері, оскільки нативно підтримується JavaScript API.",[5280,9691,9692],{},[5232,9693,9694],{},"gRPC Bidirectional Streaming:",[5198,9696,9697,9703,9706,9709,9712],{},[5201,9698,9699,9700,9702],{},"Строго типізовані контракти, визначені у ",[5211,9701,5213],{}," файлах.",[5201,9704,9705],{},"Автоматична кодогенерація для клієнтів та серверів на десятках мов.",[5201,9707,9708],{},"Вбудована підтримка таймаутів, метаданих, інтерцепторів, балансування навантаження.",[5201,9710,9711],{},"Інтеграція з екосистемою Protobuf та HTTP\u002F2.",[5201,9713,9714],{},"Оптимальний для міжсервісної комунікації у бекенді.",[5280,9716,9717,9720],{},[5232,9718,9719],{},"Висновок:"," У мікросервісній архітектурі, де комунікація відбувається між серверними компонентами (Go ↔ Java ↔ Python), gRPC Streaming є кращим вибором завдяки строгості контрактів та промисловій екосистемі. WebSocket залишається оптимальним для комунікації браузер ↔ сервер.",[9660,9722,9724,9731,9737,9743,9753],{"icon":9662,"label":9723},"❓ Чи можна передавати великі файли (GB розміру) через gRPC Streaming?",[5280,9725,9726,9727,9730],{},"Технічно gRPC Streaming підтримує передачу файлів будь-якого розміру, оскільки дані передаються частинами (",[5284,9728,9729],{},"chunks","), не завантажуючись цілком у пам'ять. Однак є кілька важливих обмежень:",[5280,9732,9733,9736],{},[5232,9734,9735],{},"Обмеження розміру повідомлення:"," За замовчуванням gRPC обмежує розмір одного повідомлення до 4 МБ. Для передачі файлів потрібно розбивати їх на менші частини (наприклад, по 1 МБ) та надсилати послідовність повідомлень.",[5280,9738,9739,9742],{},[5232,9740,9741],{},"Таймаути:"," Передача великих файлів може тривати хвилини або години, тому необхідно налаштувати відповідні таймаути на клієнті та сервері або взагалі вимкнути їх для streaming-операцій.",[5280,9744,9745,9748,9749,9752],{},[5232,9746,9747],{},"Відсутність resumable upload:"," Якщо з'єднання розривається у середині передачі, клієнт повинен починати завантаження заново. Для надійної передачі великих файлів рекомендується використовувати спеціалізовані протоколи (наприклад, ",[5211,9750,9751],{},"tus.io"," resumable upload protocol) або S3-сумісні об'єктні сховища.",[5280,9754,9755,9758,9759,9762],{},[5232,9756,9757],{},"Рекомендація:"," Використовуйте gRPC Streaming для файлів розміром до 100–500 МБ. Для більших файлів (відео, резервні копії, датасети машинного навчання) використовуйте об'єктні сховища (AWS S3, Google Cloud Storage, MinIO) із підписаними URL (",[5284,9760,9761],{},"presigned URLs",") для прямого завантаження.",[5267,9764],{},[5270,9766,9768],{"id":9767},"реалізація-grpc-сервера-у-go","Реалізація gRPC-сервера у Go",[5275,9770,9772],{"id":9771},"структура-серверного-застосунку","Структура серверного застосунку",[5280,9774,9775,9776,9778,9779,5214],{},"Після компіляції ",[5211,9777,5213],{}," файлів отримуємо згенерований інтерфейс сервісу, який потрібно реалізувати у Go-коді. Розглянемо повну реалізацію серверної частини для ",[5211,9780,8985],{},[5280,9782,9783],{},[5232,9784,9785],{},"Крок 1: Визначення структури сервера",[5456,9787,9789],{"className":5458,"code":9788,"language":5460,"meta":5461,"style":5461},"\u002F\u002F server\u002Fuser_service.go\npackage main\n\nimport (\n    \"context\"\n    \"errors\"\n    \"log\"\n    \"sync\"\n    \n    pb \"github.com\u002Fexample\u002Fuserservice\u002Fpb\"\n    \"google.golang.org\u002Fgrpc\u002Fcodes\"\n    \"google.golang.org\u002Fgrpc\u002Fstatus\"\n)\n\n\u002F\u002F UserServer реалізує інтерфейс pb.UserServiceServer\ntype UserServer struct {\n    pb.UnimplementedUserServiceServer  \u002F\u002F Вбудовування для прямої сумісності\n    \n    mu    sync.RWMutex\n    users map[int64]*pb.User  \u002F\u002F In-memory сховище для демонстрації\n    nextID int64\n}\n\n\u002F\u002F NewUserServer створює новий екземпляр сервера\nfunc NewUserServer() *UserServer {\n    return &UserServer{\n        users: make(map[int64]*pb.User),\n        nextID: 1,\n    }\n}\n",[5211,9790,9791,9796,9804,9808,9815,9820,9825,9830,9835,9839,9847,9852,9857,9861,9865,9870,9881,9893,9897,9910,9936,9944,9948,9952,9957,9972,9983,10011,10023,10027],{"__ignoreMap":5461},[5465,9792,9793],{"class":5467,"line":5468},[5465,9794,9795],{"class":5471},"\u002F\u002F server\u002Fuser_service.go\n",[5465,9797,9798,9801],{"class":5467,"line":5475},[5465,9799,9800],{"class":5478},"package",[5465,9802,9803],{"class":5482}," main\n",[5465,9805,9806],{"class":5467,"line":5493},[5465,9807,5529],{"emptyLinePlaceholder":5528},[5465,9809,9810,9813],{"class":5467,"line":5507},[5465,9811,9812],{"class":5675},"import",[5465,9814,7535],{"class":5489},[5465,9816,9817],{"class":5467,"line":5519},[5465,9818,9819],{"class":5503},"    \"context\"\n",[5465,9821,9822],{"class":5467,"line":5525},[5465,9823,9824],{"class":5503},"    \"errors\"\n",[5465,9826,9827],{"class":5467,"line":5532},[5465,9828,9829],{"class":5503},"    \"log\"\n",[5465,9831,9832],{"class":5467,"line":5565},[5465,9833,9834],{"class":5503},"    \"sync\"\n",[5465,9836,9837],{"class":5467,"line":5594},[5465,9838,5597],{"class":5489},[5465,9840,9841,9844],{"class":5467,"line":5600},[5465,9842,9843],{"class":5496},"    pb",[5465,9845,9846],{"class":5503}," \"github.com\u002Fexample\u002Fuserservice\u002Fpb\"\n",[5465,9848,9849],{"class":5467,"line":5624},[5465,9850,9851],{"class":5503},"    \"google.golang.org\u002Fgrpc\u002Fcodes\"\n",[5465,9853,9854],{"class":5467,"line":5639},[5465,9855,9856],{"class":5503},"    \"google.golang.org\u002Fgrpc\u002Fstatus\"\n",[5465,9858,9859],{"class":5467,"line":5647},[5465,9860,5591],{"class":5489},[5465,9862,9863],{"class":5467,"line":5666},[5465,9864,5529],{"emptyLinePlaceholder":5528},[5465,9866,9867],{"class":5467,"line":5672},[5465,9868,9869],{"class":5471},"\u002F\u002F UserServer реалізує інтерфейс pb.UserServiceServer\n",[5465,9871,9872,9874,9877,9879],{"class":5467,"line":5690},[5465,9873,5479],{"class":5478},[5465,9875,9876],{"class":5482}," UserServer",[5465,9878,5486],{"class":5478},[5465,9880,5490],{"class":5489},[5465,9882,9883,9885,9887,9890],{"class":5467,"line":5699},[5465,9884,9843],{"class":5482},[5465,9886,5214],{"class":5489},[5465,9888,9889],{"class":5482},"UnimplementedUserServiceServer",[5465,9891,9892],{"class":5471},"  \u002F\u002F Вбудовування для прямої сумісності\n",[5465,9894,9895],{"class":5467,"line":5705},[5465,9896,5597],{"class":5489},[5465,9898,9899,9902,9905,9907],{"class":5467,"line":5727},[5465,9900,9901],{"class":5496},"    mu",[5465,9903,9904],{"class":5482},"    sync",[5465,9906,5214],{"class":5489},[5465,9908,9909],{"class":5482},"RWMutex\n",[5465,9911,9912,9915,9918,9921,9923,9926,9929,9931,9933],{"class":5467,"line":5732},[5465,9913,9914],{"class":5496},"    users",[5465,9916,9917],{"class":5478}," map",[5465,9919,9920],{"class":5489},"[",[5465,9922,6761],{"class":5482},[5465,9924,9925],{"class":5489},"]*",[5465,9927,9928],{"class":5482},"pb",[5465,9930,5214],{"class":5489},[5465,9932,5923],{"class":5482},[5465,9934,9935],{"class":5471},"  \u002F\u002F In-memory сховище для демонстрації\n",[5465,9937,9938,9941],{"class":5467,"line":5755},[5465,9939,9940],{"class":5496},"    nextID",[5465,9942,9943],{"class":5482}," int64\n",[5465,9945,9946],{"class":5467,"line":5786},[5465,9947,5522],{"class":5489},[5465,9949,9950],{"class":5467,"line":5831},[5465,9951,5529],{"emptyLinePlaceholder":5528},[5465,9953,9954],{"class":5467,"line":5836},[5465,9955,9956],{"class":5471},"\u002F\u002F NewUserServer створює новий екземпляр сервера\n",[5465,9958,9959,9961,9964,9967,9970],{"class":5467,"line":5841},[5465,9960,5535],{"class":5478},[5465,9962,9963],{"class":5538}," NewUserServer",[5465,9965,9966],{"class":5489},"() *",[5465,9968,9969],{"class":5482},"UserServer",[5465,9971,5490],{"class":5489},[5465,9973,9974,9976,9979,9981],{"class":5467,"line":5850},[5465,9975,5844],{"class":5675},[5465,9977,9978],{"class":5489}," &",[5465,9980,9969],{"class":5482},[5465,9982,5926],{"class":5489},[5465,9984,9985,9988,9990,9993,9995,9997,9999,10001,10003,10005,10007,10009],{"class":5467,"line":6213},[5465,9986,9987],{"class":5496},"        users",[5465,9989,5807],{"class":5489},[5465,9991,9992],{"class":5538},"make",[5465,9994,5542],{"class":5489},[5465,9996,7078],{"class":5478},[5465,9998,9920],{"class":5489},[5465,10000,6761],{"class":5482},[5465,10002,9925],{"class":5489},[5465,10004,9928],{"class":5482},[5465,10006,5214],{"class":5489},[5465,10008,5923],{"class":5482},[5465,10010,5663],{"class":5489},[5465,10012,10013,10016,10018,10021],{"class":5467,"line":6218},[5465,10014,10015],{"class":5496},"        nextID",[5465,10017,5807],{"class":5489},[5465,10019,10020],{"class":7522},"1",[5465,10022,5636],{"class":5489},[5465,10024,10025],{"class":5467,"line":6224},[5465,10026,5702],{"class":5489},[5465,10028,10029],{"class":5467,"line":6230},[5465,10030,5522],{"class":5489},[5280,10032,10033],{},[5232,10034,10035],{},"Пояснення:",[5280,10037,10038,10041,10042,10045,10046,10049],{},[5232,10039,10040],{},"Рядок 17:"," Вбудовування ",[5211,10043,10044],{},"pb.UnimplementedUserServiceServer"," є обов'язковою практикою у сучасних версіях gRPC. Ця структура містить заглушки (",[5284,10047,10048],{},"stubs",") для всіх методів сервісу, що дозволяє компілювати код навіть якщо ви ще не реалізували всі методи. Без цього вбудовування компілятор видасть помилку «not all methods implemented».",[5280,10051,10052,10055,10056,10059,10060,10062],{},[5232,10053,10054],{},"Рядки 19–21:"," Використовуємо ",[5211,10057,10058],{},"sync.RWMutex"," для потокобезпечного доступу до ",[5211,10061,7078],{},", оскільки gRPC обробляє кожен виклик у окремій горутині. У продакшн-системі замість in-memory мапи використовується база даних (PostgreSQL, MongoDB).",[5280,10064,10065],{},[5232,10066,10067,10068],{},"Крок 2: Реалізація Unary RPC методу ",[5211,10069,5912],{},[5456,10071,10073],{"className":5458,"code":10072,"language":5460,"meta":5461,"style":5461},"\u002F\u002F CreateUser реалізує Unary RPC для створення користувача\nfunc (s *UserServer) CreateUser(\n    ctx context.Context,\n    req *pb.CreateUserRequest,\n) (*pb.CreateUserResponse, error) {\n    \u002F\u002F Валідація вхідних даних\n    if req.GetName() == \"\" {\n        return nil, status.Error(codes.InvalidArgument, \"name is required\")\n    }\n    if req.GetEmail() == \"\" {\n        return nil, status.Error(codes.InvalidArgument, \"email is required\")\n    }\n    \n    \u002F\u002F Перевірка таймауту контексту\n    select {\n    case \u003C-ctx.Done():\n        return nil, status.Error(codes.DeadlineExceeded, \"request timeout\")\n    default:\n    }\n    \n    \u002F\u002F Створення нового користувача\n    s.mu.Lock()\n    defer s.mu.Unlock()\n    \n    user := &pb.User{\n        Id:        s.nextID,\n        Name:      req.GetName(),\n        Email:     req.GetEmail(),\n        Status:    pb.UserStatus_USER_STATUS_ACTIVE,\n        CreatedAt: 0, \u002F\u002F У реальній системі: time.Now().Unix()\n    }\n    \n    s.users[s.nextID] = user\n    s.nextID++\n    \n    log.Printf(\"Created user: id=%d, name=%s\", user.Id, user.Name)\n    \n    return &pb.CreateUserResponse{User: user}, nil\n}\n",[5211,10074,10075,10080,10100,10115,10131,10149,10154,10173,10207,10211,10228,10257,10261,10265,10270,10277,10295,10325,10333,10337,10341,10346,10363,10381,10385,10401,10419,10437,10453,10470,10484,10488,10492,10515,10526,10530,10574,10578,10605],{"__ignoreMap":5461},[5465,10076,10077],{"class":5467,"line":5468},[5465,10078,10079],{"class":5471},"\u002F\u002F CreateUser реалізує Unary RPC для створення користувача\n",[5465,10081,10082,10084,10086,10089,10092,10094,10096,10098],{"class":5467,"line":5475},[5465,10083,5535],{"class":5478},[5465,10085,5321],{"class":5489},[5465,10087,10088],{"class":5496},"s ",[5465,10090,10091],{"class":5489},"*",[5465,10093,9969],{"class":5482},[5465,10095,5557],{"class":5489},[5465,10097,5912],{"class":5538},[5465,10099,5621],{"class":5489},[5465,10101,10102,10105,10108,10110,10113],{"class":5467,"line":5493},[5465,10103,10104],{"class":5496},"    ctx",[5465,10106,10107],{"class":5482}," context",[5465,10109,5214],{"class":5489},[5465,10111,10112],{"class":5482},"Context",[5465,10114,5636],{"class":5489},[5465,10116,10117,10120,10123,10125,10127,10129],{"class":5467,"line":5507},[5465,10118,10119],{"class":5496},"    req",[5465,10121,10122],{"class":5489}," *",[5465,10124,9928],{"class":5482},[5465,10126,5214],{"class":5489},[5465,10128,9007],{"class":5482},[5465,10130,5636],{"class":5489},[5465,10132,10133,10136,10138,10140,10142,10144,10146],{"class":5467,"line":5519},[5465,10134,10135],{"class":5489},") (*",[5465,10137,9928],{"class":5482},[5465,10139,5214],{"class":5489},[5465,10141,9017],{"class":5482},[5465,10143,5336],{"class":5489},[5465,10145,5560],{"class":5482},[5465,10147,10148],{"class":5489},") {\n",[5465,10150,10151],{"class":5467,"line":5525},[5465,10152,10153],{"class":5471},"    \u002F\u002F Валідація вхідних даних\n",[5465,10155,10156,10158,10161,10163,10166,10169,10171],{"class":5467,"line":5532},[5465,10157,5676],{"class":5675},[5465,10159,10160],{"class":5496}," req",[5465,10162,5214],{"class":5489},[5465,10164,10165],{"class":5538},"GetName",[5465,10167,10168],{"class":5489},"() == ",[5465,10170,7328],{"class":5503},[5465,10172,5490],{"class":5489},[5465,10174,10175,10177,10180,10182,10185,10187,10190,10192,10195,10197,10200,10202,10205],{"class":5467,"line":5565},[5465,10176,5693],{"class":5675},[5465,10178,10179],{"class":5478}," nil",[5465,10181,5336],{"class":5489},[5465,10183,10184],{"class":5496},"status",[5465,10186,5214],{"class":5489},[5465,10188,10189],{"class":5538},"Error",[5465,10191,5542],{"class":5489},[5465,10193,10194],{"class":5496},"codes",[5465,10196,5214],{"class":5489},[5465,10198,10199],{"class":5496},"InvalidArgument",[5465,10201,5336],{"class":5489},[5465,10203,10204],{"class":5503},"\"name is required\"",[5465,10206,5591],{"class":5489},[5465,10208,10209],{"class":5467,"line":5594},[5465,10210,5702],{"class":5489},[5465,10212,10213,10215,10217,10219,10222,10224,10226],{"class":5467,"line":5600},[5465,10214,5676],{"class":5675},[5465,10216,10160],{"class":5496},[5465,10218,5214],{"class":5489},[5465,10220,10221],{"class":5538},"GetEmail",[5465,10223,10168],{"class":5489},[5465,10225,7328],{"class":5503},[5465,10227,5490],{"class":5489},[5465,10229,10230,10232,10234,10236,10238,10240,10242,10244,10246,10248,10250,10252,10255],{"class":5467,"line":5624},[5465,10231,5693],{"class":5675},[5465,10233,10179],{"class":5478},[5465,10235,5336],{"class":5489},[5465,10237,10184],{"class":5496},[5465,10239,5214],{"class":5489},[5465,10241,10189],{"class":5538},[5465,10243,5542],{"class":5489},[5465,10245,10194],{"class":5496},[5465,10247,5214],{"class":5489},[5465,10249,10199],{"class":5496},[5465,10251,5336],{"class":5489},[5465,10253,10254],{"class":5503},"\"email is required\"",[5465,10256,5591],{"class":5489},[5465,10258,10259],{"class":5467,"line":5639},[5465,10260,5702],{"class":5489},[5465,10262,10263],{"class":5467,"line":5647},[5465,10264,5597],{"class":5489},[5465,10266,10267],{"class":5467,"line":5666},[5465,10268,10269],{"class":5471},"    \u002F\u002F Перевірка таймауту контексту\n",[5465,10271,10272,10275],{"class":5467,"line":5672},[5465,10273,10274],{"class":5675},"    select",[5465,10276,5490],{"class":5489},[5465,10278,10279,10282,10285,10287,10289,10292],{"class":5467,"line":5690},[5465,10280,10281],{"class":5675},"    case",[5465,10283,10284],{"class":5489}," \u003C-",[5465,10286,5917],{"class":5496},[5465,10288,5214],{"class":5489},[5465,10290,10291],{"class":5538},"Done",[5465,10293,10294],{"class":5489},"():\n",[5465,10296,10297,10299,10301,10303,10305,10307,10309,10311,10313,10315,10318,10320,10323],{"class":5467,"line":5699},[5465,10298,5693],{"class":5675},[5465,10300,10179],{"class":5478},[5465,10302,5336],{"class":5489},[5465,10304,10184],{"class":5496},[5465,10306,5214],{"class":5489},[5465,10308,10189],{"class":5538},[5465,10310,5542],{"class":5489},[5465,10312,10194],{"class":5496},[5465,10314,5214],{"class":5489},[5465,10316,10317],{"class":5496},"DeadlineExceeded",[5465,10319,5336],{"class":5489},[5465,10321,10322],{"class":5503},"\"request timeout\"",[5465,10324,5591],{"class":5489},[5465,10326,10327,10330],{"class":5467,"line":5705},[5465,10328,10329],{"class":5675},"    default",[5465,10331,10332],{"class":5489},":\n",[5465,10334,10335],{"class":5467,"line":5727},[5465,10336,5702],{"class":5489},[5465,10338,10339],{"class":5467,"line":5732},[5465,10340,5597],{"class":5489},[5465,10342,10343],{"class":5467,"line":5755},[5465,10344,10345],{"class":5471},"    \u002F\u002F Створення нового користувача\n",[5465,10347,10348,10351,10353,10356,10358,10361],{"class":5467,"line":5786},[5465,10349,10350],{"class":5496},"    s",[5465,10352,5214],{"class":5489},[5465,10354,10355],{"class":5496},"mu",[5465,10357,5214],{"class":5489},[5465,10359,10360],{"class":5538},"Lock",[5465,10362,5724],{"class":5489},[5465,10364,10365,10367,10370,10372,10374,10376,10379],{"class":5467,"line":5831},[5465,10366,5708],{"class":5675},[5465,10368,10369],{"class":5496}," s",[5465,10371,5214],{"class":5489},[5465,10373,10355],{"class":5496},[5465,10375,5214],{"class":5489},[5465,10377,10378],{"class":5538},"Unlock",[5465,10380,5724],{"class":5489},[5465,10382,10383],{"class":5467,"line":5836},[5465,10384,5597],{"class":5489},[5465,10386,10387,10390,10393,10395,10397,10399],{"class":5467,"line":5841},[5465,10388,10389],{"class":5496},"    user",[5465,10391,10392],{"class":5489}," := &",[5465,10394,9928],{"class":5482},[5465,10396,5214],{"class":5489},[5465,10398,5923],{"class":5482},[5465,10400,5926],{"class":5489},[5465,10402,10403,10406,10409,10412,10414,10417],{"class":5467,"line":5850},[5465,10404,10405],{"class":5496},"        Id",[5465,10407,10408],{"class":5489},":        ",[5465,10410,10411],{"class":5496},"s",[5465,10413,5214],{"class":5489},[5465,10415,10416],{"class":5496},"nextID",[5465,10418,5636],{"class":5489},[5465,10420,10421,10424,10427,10430,10432,10434],{"class":5467,"line":6213},[5465,10422,10423],{"class":5496},"        Name",[5465,10425,10426],{"class":5489},":      ",[5465,10428,10429],{"class":5496},"req",[5465,10431,5214],{"class":5489},[5465,10433,10165],{"class":5538},[5465,10435,10436],{"class":5489},"(),\n",[5465,10438,10439,10442,10445,10447,10449,10451],{"class":5467,"line":6218},[5465,10440,10441],{"class":5496},"        Email",[5465,10443,10444],{"class":5489},":     ",[5465,10446,10429],{"class":5496},[5465,10448,5214],{"class":5489},[5465,10450,10221],{"class":5538},[5465,10452,10436],{"class":5489},[5465,10454,10455,10458,10461,10463,10465,10468],{"class":5467,"line":6224},[5465,10456,10457],{"class":5496},"        Status",[5465,10459,10460],{"class":5489},":    ",[5465,10462,9928],{"class":5496},[5465,10464,5214],{"class":5489},[5465,10466,10467],{"class":5496},"UserStatus_USER_STATUS_ACTIVE",[5465,10469,5636],{"class":5489},[5465,10471,10472,10475,10477,10479,10481],{"class":5467,"line":6230},[5465,10473,10474],{"class":5496},"        CreatedAt",[5465,10476,5807],{"class":5489},[5465,10478,6820],{"class":7522},[5465,10480,5336],{"class":5489},[5465,10482,10483],{"class":5471},"\u002F\u002F У реальній системі: time.Now().Unix()\n",[5465,10485,10486],{"class":5467,"line":6236},[5465,10487,5702],{"class":5489},[5465,10489,10490],{"class":5467,"line":6242},[5465,10491,5597],{"class":5489},[5465,10493,10494,10496,10498,10501,10503,10505,10507,10509,10512],{"class":5467,"line":6248},[5465,10495,10350],{"class":5496},[5465,10497,5214],{"class":5489},[5465,10499,10500],{"class":5496},"users",[5465,10502,9920],{"class":5489},[5465,10504,10411],{"class":5496},[5465,10506,5214],{"class":5489},[5465,10508,10416],{"class":5496},[5465,10510,10511],{"class":5489},"] = ",[5465,10513,10514],{"class":5496},"user\n",[5465,10516,10517,10519,10521,10523],{"class":5467,"line":6254},[5465,10518,10350],{"class":5496},[5465,10520,5214],{"class":5489},[5465,10522,10416],{"class":5496},[5465,10524,10525],{"class":5489},"++\n",[5465,10527,10528],{"class":5467,"line":6260},[5465,10529,5597],{"class":5489},[5465,10531,10532,10535,10537,10540,10542,10545,10547,10550,10552,10554,10556,10558,10560,10563,10565,10567,10569,10572],{"class":5467,"line":6265},[5465,10533,10534],{"class":5496},"    log",[5465,10536,5214],{"class":5489},[5465,10538,10539],{"class":5538},"Printf",[5465,10541,5542],{"class":5489},[5465,10543,10544],{"class":5503},"\"Created user: id=",[5465,10546,5804],{"class":5496},[5465,10548,10549],{"class":5503},", name=",[5465,10551,5810],{"class":5496},[5465,10553,5813],{"class":5503},[5465,10555,5336],{"class":5489},[5465,10557,5552],{"class":5496},[5465,10559,5214],{"class":5489},[5465,10561,10562],{"class":5496},"Id",[5465,10564,5336],{"class":5489},[5465,10566,5552],{"class":5496},[5465,10568,5214],{"class":5489},[5465,10570,10571],{"class":5496},"Name",[5465,10573,5591],{"class":5489},[5465,10575,10576],{"class":5467,"line":6270},[5465,10577,5597],{"class":5489},[5465,10579,10580,10582,10584,10586,10588,10590,10593,10595,10597,10599,10602],{"class":5467,"line":6276},[5465,10581,5844],{"class":5675},[5465,10583,9978],{"class":5489},[5465,10585,9928],{"class":5482},[5465,10587,5214],{"class":5489},[5465,10589,9017],{"class":5482},[5465,10591,10592],{"class":5489},"{",[5465,10594,5923],{"class":5496},[5465,10596,5807],{"class":5489},[5465,10598,5552],{"class":5496},[5465,10600,10601],{"class":5489},"}, ",[5465,10603,10604],{"class":5478},"nil\n",[5465,10606,10607],{"class":5467,"line":6282},[5465,10608,5522],{"class":5489},[5280,10610,10611],{},[5232,10612,10613],{},"Ключові аспекти реалізації:",[5280,10615,10616,10619,10620,10623],{},[5232,10617,10618],{},"Рядки 7–12:"," Валідація вхідних параметрів є критичною для запобігання некоректному стану системи. gRPC використовує пакет ",[5211,10621,10622],{},"google.golang.org\u002Fgrpc\u002Fstatus"," для повернення стандартизованих помилок із кодами (аналогічно до HTTP статус-кодів).",[5280,10625,10626,10629,10630,10633],{},[5232,10627,10628],{},"Рядки 15–19:"," Перевірка контексту ",[5211,10631,10632],{},"ctx.Done()"," дозволяє коректно обробляти ситуації, коли клієнт скасував запит або спливтаймаут. Якщо контекст закрито, подальша обробка марна — краще негайно повернути помилку.",[5280,10635,10636,10639,10640,10643,10644,10647],{},[5232,10637,10638],{},"Рядки 22–34:"," Захоплення м'ютексу для атомарної модифікації мапи. У реальній системі тут буде виклик ",[5211,10641,10642],{},"userRepository.Create(ctx, user)",", що виконає ",[5211,10645,10646],{},"INSERT"," запит до PostgreSQL.",[5280,10649,10650,10653,10654,10656],{},[5232,10651,10652],{},"Рядок 38:"," Повертаємо успішну відповідь. gRPC автоматично серіалізує структуру ",[5211,10655,9017],{}," у бінарний формат Protobuf та відправляє клієнту через HTTP\u002F2.",[5280,10658,10659],{},[5232,10660,10661,10662],{},"Крок 3: Реалізація Server Streaming RPC методу ",[5211,10663,10664],{},"ListUsers",[5456,10666,10668],{"className":5458,"code":10667,"language":5460,"meta":5461,"style":5461},"\u002F\u002F ListUsers реалізує Server Streaming RPC для відправки списку користувачів\nfunc (s *UserServer) ListUsers(\n    req *pb.ListUsersRequest,\n    stream pb.UserService_ListUsersServer,\n) error {\n    s.mu.RLock()\n    defer s.mu.RUnlock()\n    \n    log.Printf(\"Streaming users list to client, total: %d\", len(s.users))\n    \n    \u002F\u002F Ітеруємось по всіх користувачах та надсилаємо по одному\n    for id, user := range s.users {\n        \u002F\u002F Перевірка, чи клієнт не розірвав з'єднання\n        if err := stream.Context().Err(); err != nil {\n            log.Printf(\"Client disconnected: %v\", err)\n            return status.Error(codes.Canceled, \"client disconnected\")\n        }\n        \n        \u002F\u002F Надсилаємо користувача через потік\n        if err := stream.Send(user); err != nil {\n            log.Printf(\"Failed to send user id=%d: %v\", id, err)\n            return status.Errorf(codes.Internal, \"stream error: %v\", err)\n        }\n        \n        log.Printf(\"Sent user: id=%d, name=%s\", user.Id, user.Name)\n    }\n    \n    log.Println(\"Completed streaming users list\")\n    return nil  \u002F\u002F Повернення nil закриває потік успішно\n}\n",[5211,10669,10670,10675,10693,10708,10723,10731,10746,10763,10767,10800,10804,10809,10834,10839,10871,10896,10924,10929,10934,10939,10969,11000,11034,11038,11042,11082,11086,11090,11106,11115],{"__ignoreMap":5461},[5465,10671,10672],{"class":5467,"line":5468},[5465,10673,10674],{"class":5471},"\u002F\u002F ListUsers реалізує Server Streaming RPC для відправки списку користувачів\n",[5465,10676,10677,10679,10681,10683,10685,10687,10689,10691],{"class":5467,"line":5475},[5465,10678,5535],{"class":5478},[5465,10680,5321],{"class":5489},[5465,10682,10088],{"class":5496},[5465,10684,10091],{"class":5489},[5465,10686,9969],{"class":5482},[5465,10688,5557],{"class":5489},[5465,10690,10664],{"class":5538},[5465,10692,5621],{"class":5489},[5465,10694,10695,10697,10699,10701,10703,10706],{"class":5467,"line":5493},[5465,10696,10119],{"class":5496},[5465,10698,10122],{"class":5489},[5465,10700,9928],{"class":5482},[5465,10702,5214],{"class":5489},[5465,10704,10705],{"class":5482},"ListUsersRequest",[5465,10707,5636],{"class":5489},[5465,10709,10710,10713,10716,10718,10721],{"class":5467,"line":5507},[5465,10711,10712],{"class":5496},"    stream",[5465,10714,10715],{"class":5482}," pb",[5465,10717,5214],{"class":5489},[5465,10719,10720],{"class":5482},"UserService_ListUsersServer",[5465,10722,5636],{"class":5489},[5465,10724,10725,10727,10729],{"class":5467,"line":5519},[5465,10726,5557],{"class":5489},[5465,10728,5560],{"class":5482},[5465,10730,5490],{"class":5489},[5465,10732,10733,10735,10737,10739,10741,10744],{"class":5467,"line":5525},[5465,10734,10350],{"class":5496},[5465,10736,5214],{"class":5489},[5465,10738,10355],{"class":5496},[5465,10740,5214],{"class":5489},[5465,10742,10743],{"class":5538},"RLock",[5465,10745,5724],{"class":5489},[5465,10747,10748,10750,10752,10754,10756,10758,10761],{"class":5467,"line":5532},[5465,10749,5708],{"class":5675},[5465,10751,10369],{"class":5496},[5465,10753,5214],{"class":5489},[5465,10755,10355],{"class":5496},[5465,10757,5214],{"class":5489},[5465,10759,10760],{"class":5538},"RUnlock",[5465,10762,5724],{"class":5489},[5465,10764,10765],{"class":5467,"line":5565},[5465,10766,5597],{"class":5489},[5465,10768,10769,10771,10773,10775,10777,10780,10782,10784,10786,10789,10791,10793,10795,10797],{"class":5467,"line":5594},[5465,10770,10534],{"class":5496},[5465,10772,5214],{"class":5489},[5465,10774,10539],{"class":5538},[5465,10776,5542],{"class":5489},[5465,10778,10779],{"class":5503},"\"Streaming users list to client, total: ",[5465,10781,5804],{"class":5496},[5465,10783,5813],{"class":5503},[5465,10785,5336],{"class":5489},[5465,10787,10788],{"class":5538},"len",[5465,10790,5542],{"class":5489},[5465,10792,10411],{"class":5496},[5465,10794,5214],{"class":5489},[5465,10796,10500],{"class":5496},[5465,10798,10799],{"class":5489},"))\n",[5465,10801,10802],{"class":5467,"line":5600},[5465,10803,5597],{"class":5489},[5465,10805,10806],{"class":5467,"line":5624},[5465,10807,10808],{"class":5471},"    \u002F\u002F Ітеруємось по всіх користувачах та надсилаємо по одному\n",[5465,10810,10811,10814,10817,10819,10821,10823,10826,10828,10830,10832],{"class":5467,"line":5639},[5465,10812,10813],{"class":5675},"    for",[5465,10815,10816],{"class":5496}," id",[5465,10818,5336],{"class":5489},[5465,10820,5552],{"class":5496},[5465,10822,5576],{"class":5489},[5465,10824,10825],{"class":5675},"range",[5465,10827,10369],{"class":5496},[5465,10829,5214],{"class":5489},[5465,10831,10500],{"class":5496},[5465,10833,5490],{"class":5489},[5465,10835,10836],{"class":5467,"line":5647},[5465,10837,10838],{"class":5471},"        \u002F\u002F Перевірка, чи клієнт не розірвав з'єднання\n",[5465,10840,10841,10844,10846,10848,10850,10852,10854,10857,10860,10863,10865,10867,10869],{"class":5467,"line":5666},[5465,10842,10843],{"class":5675},"        if",[5465,10845,5679],{"class":5496},[5465,10847,5576],{"class":5489},[5465,10849,9028],{"class":5496},[5465,10851,5214],{"class":5489},[5465,10853,10112],{"class":5538},[5465,10855,10856],{"class":5489},"().",[5465,10858,10859],{"class":5538},"Err",[5465,10861,10862],{"class":5489},"(); ",[5465,10864,5608],{"class":5496},[5465,10866,5682],{"class":5489},[5465,10868,5685],{"class":5478},[5465,10870,5490],{"class":5489},[5465,10872,10873,10876,10878,10880,10882,10885,10888,10890,10892,10894],{"class":5467,"line":5672},[5465,10874,10875],{"class":5496},"            log",[5465,10877,5214],{"class":5489},[5465,10879,10539],{"class":5538},[5465,10881,5542],{"class":5489},[5465,10883,10884],{"class":5503},"\"Client disconnected: ",[5465,10886,10887],{"class":5496},"%v",[5465,10889,5813],{"class":5503},[5465,10891,5336],{"class":5489},[5465,10893,5608],{"class":5496},[5465,10895,5591],{"class":5489},[5465,10897,10898,10901,10904,10906,10908,10910,10912,10914,10917,10919,10922],{"class":5467,"line":5690},[5465,10899,10900],{"class":5675},"            return",[5465,10902,10903],{"class":5496}," status",[5465,10905,5214],{"class":5489},[5465,10907,10189],{"class":5538},[5465,10909,5542],{"class":5489},[5465,10911,10194],{"class":5496},[5465,10913,5214],{"class":5489},[5465,10915,10916],{"class":5496},"Canceled",[5465,10918,5336],{"class":5489},[5465,10920,10921],{"class":5503},"\"client disconnected\"",[5465,10923,5591],{"class":5489},[5465,10925,10926],{"class":5467,"line":5699},[5465,10927,10928],{"class":5489},"        }\n",[5465,10930,10931],{"class":5467,"line":5705},[5465,10932,10933],{"class":5489},"        \n",[5465,10935,10936],{"class":5467,"line":5727},[5465,10937,10938],{"class":5471},"        \u002F\u002F Надсилаємо користувача через потік\n",[5465,10940,10941,10943,10945,10947,10949,10951,10954,10956,10958,10961,10963,10965,10967],{"class":5467,"line":5732},[5465,10942,10843],{"class":5675},[5465,10944,5679],{"class":5496},[5465,10946,5576],{"class":5489},[5465,10948,9028],{"class":5496},[5465,10950,5214],{"class":5489},[5465,10952,10953],{"class":5538},"Send",[5465,10955,5542],{"class":5489},[5465,10957,5552],{"class":5496},[5465,10959,10960],{"class":5489},"); ",[5465,10962,5608],{"class":5496},[5465,10964,5682],{"class":5489},[5465,10966,5685],{"class":5478},[5465,10968,5490],{"class":5489},[5465,10970,10971,10973,10975,10977,10979,10982,10984,10986,10988,10990,10992,10994,10996,10998],{"class":5467,"line":5755},[5465,10972,10875],{"class":5496},[5465,10974,5214],{"class":5489},[5465,10976,10539],{"class":5538},[5465,10978,5542],{"class":5489},[5465,10980,10981],{"class":5503},"\"Failed to send user id=",[5465,10983,5804],{"class":5496},[5465,10985,5807],{"class":5503},[5465,10987,10887],{"class":5496},[5465,10989,5813],{"class":5503},[5465,10991,5336],{"class":5489},[5465,10993,6776],{"class":5496},[5465,10995,5336],{"class":5489},[5465,10997,5608],{"class":5496},[5465,10999,5591],{"class":5489},[5465,11001,11002,11004,11006,11008,11010,11012,11014,11016,11019,11021,11024,11026,11028,11030,11032],{"class":5467,"line":5786},[5465,11003,10900],{"class":5675},[5465,11005,10903],{"class":5496},[5465,11007,5214],{"class":5489},[5465,11009,5796],{"class":5538},[5465,11011,5542],{"class":5489},[5465,11013,10194],{"class":5496},[5465,11015,5214],{"class":5489},[5465,11017,11018],{"class":5496},"Internal",[5465,11020,5336],{"class":5489},[5465,11022,11023],{"class":5503},"\"stream error: ",[5465,11025,10887],{"class":5496},[5465,11027,5813],{"class":5503},[5465,11029,5336],{"class":5489},[5465,11031,5608],{"class":5496},[5465,11033,5591],{"class":5489},[5465,11035,11036],{"class":5467,"line":5831},[5465,11037,10928],{"class":5489},[5465,11039,11040],{"class":5467,"line":5836},[5465,11041,10933],{"class":5489},[5465,11043,11044,11047,11049,11051,11053,11056,11058,11060,11062,11064,11066,11068,11070,11072,11074,11076,11078,11080],{"class":5467,"line":5841},[5465,11045,11046],{"class":5496},"        log",[5465,11048,5214],{"class":5489},[5465,11050,10539],{"class":5538},[5465,11052,5542],{"class":5489},[5465,11054,11055],{"class":5503},"\"Sent user: id=",[5465,11057,5804],{"class":5496},[5465,11059,10549],{"class":5503},[5465,11061,5810],{"class":5496},[5465,11063,5813],{"class":5503},[5465,11065,5336],{"class":5489},[5465,11067,5552],{"class":5496},[5465,11069,5214],{"class":5489},[5465,11071,10562],{"class":5496},[5465,11073,5336],{"class":5489},[5465,11075,5552],{"class":5496},[5465,11077,5214],{"class":5489},[5465,11079,10571],{"class":5496},[5465,11081,5591],{"class":5489},[5465,11083,11084],{"class":5467,"line":5850},[5465,11085,5702],{"class":5489},[5465,11087,11088],{"class":5467,"line":6213},[5465,11089,5597],{"class":5489},[5465,11091,11092,11094,11096,11099,11101,11104],{"class":5467,"line":6218},[5465,11093,10534],{"class":5496},[5465,11095,5214],{"class":5489},[5465,11097,11098],{"class":5538},"Println",[5465,11100,5542],{"class":5489},[5465,11102,11103],{"class":5503},"\"Completed streaming users list\"",[5465,11105,5591],{"class":5489},[5465,11107,11108,11110,11112],{"class":5467,"line":6224},[5465,11109,5844],{"class":5675},[5465,11111,10179],{"class":5478},[5465,11113,11114],{"class":5471},"  \u002F\u002F Повернення nil закриває потік успішно\n",[5465,11116,11117],{"class":5467,"line":6230},[5465,11118,5522],{"class":5489},[5280,11120,11121],{},[5232,11122,11123],{},"Відмінності від Unary RPC:",[5280,11125,11126,11129,11130,11133,11134,11136,11137,11140,11141,11144],{},[5232,11127,11128],{},"Рядок 4:"," Замість повернення відповіді через ",[5211,11131,11132],{},"return",", метод отримує об'єкт ",[5211,11135,9028],{},", що реалізує інтерфейс ",[5211,11138,11139],{},"pb.UserService_ListUsersServer",". Цей інтерфейс має метод ",[5211,11142,11143],{},"Send(*pb.User)"," для відправки кожного повідомлення.",[5280,11146,11147,11150,11151,11154,11155,11158],{},[5232,11148,11149],{},"Рядки 14–17:"," Критично важливо перевіряти ",[5211,11152,11153],{},"stream.Context().Err()"," перед кожним надсиланням. Якщо клієнт закрив з'єднання або спливтаймаут, подальші виклики ",[5211,11156,11157],{},"Send()"," завершаться помилкою.",[5280,11160,11161,11164,11165,11168],{},[5232,11162,11163],{},"Рядки 20–23:"," Метод ",[5211,11166,11167],{},"stream.Send(user)"," серіалізує повідомлення та надсилає його клієнту через HTTP\u002F2 DATA фрейм. Клієнт може починати обробку повідомлення ще до завершення ітерації на сервері.",[5280,11170,11171,11174,11175,11177],{},[5232,11172,11173],{},"Рядок 29:"," Повернення ",[5211,11176,5685],{}," сигналізує успішне завершення потоку. gRPC автоматично відправляє клієнту HTTP\u002F2 END_STREAM фрейм.",[6839,11179,11180,11181,11184,11185,11188,11189,5214],{},"У реальних системах уникайте завантаження всього списку у пам'ять перед стримінгом. Замість ",[5211,11182,11183],{},"range s.users"," використовуйте курсорну вибірку з бази даних (",[5211,11186,11187],{},"pgx.Rows","), що дозволяє обробляти мільйони записів із константним споживанням пам'яті. Приклад: ",[5211,11190,11191],{},"rows, _ := conn.Query(ctx, \"SELECT * FROM users\"); for rows.Next() { stream.Send(user) }",[5280,11193,11194],{},[5232,11195,11196],{},"Крок 4: Запуск gRPC-сервера",[5456,11198,11200],{"className":5458,"code":11199,"language":5460,"meta":5461,"style":5461},"\u002F\u002F server\u002Fmain.go\npackage main\n\nimport (\n    \"log\"\n    \"net\"\n    \n    pb \"github.com\u002Fexample\u002Fuserservice\u002Fpb\"\n    \"google.golang.org\u002Fgrpc\"\n    \"google.golang.org\u002Fgrpc\u002Freflection\"\n)\n\nfunc main() {\n    \u002F\u002F Створюємо TCP-слухач на порту 50051\n    listener, err := net.Listen(\"tcp\", \":50051\")\n    if err != nil {\n        log.Fatalf(\"Failed to listen: %v\", err)\n    }\n    \n    \u002F\u002F Створюємо gRPC-сервер\n    grpcServer := grpc.NewServer(\n        grpc.MaxRecvMsgSize(10 * 1024 * 1024),  \u002F\u002F Максимальний розмір вхідного повідомлення: 10 МБ\n        grpc.MaxSendMsgSize(10 * 1024 * 1024),  \u002F\u002F Максимальний розмір вихідного повідомлення: 10 МБ\n    )\n    \n    \u002F\u002F Реєструємо наш сервіс\n    userService := NewUserServer()\n    pb.RegisterUserServiceServer(grpcServer, userService)\n    \n    \u002F\u002F Увімкнення gRPC Reflection для інструментів типу grpcurl\n    reflection.Register(grpcServer)\n    \n    log.Println(\"gRPC server listening on :50051\")\n    \n    \u002F\u002F Блокуючий виклик: приймаємо з'єднання\n    if err := grpcServer.Serve(listener); err != nil {\n        log.Fatalf(\"Failed to serve: %v\", err)\n    }\n}\n",[5211,11201,11202,11207,11213,11217,11223,11227,11232,11236,11242,11247,11252,11256,11260,11270,11275,11306,11318,11342,11346,11350,11355,11371,11402,11428,11432,11436,11441,11453,11474,11478,11483,11499,11503,11518,11522,11527,11557,11580,11584],{"__ignoreMap":5461},[5465,11203,11204],{"class":5467,"line":5468},[5465,11205,11206],{"class":5471},"\u002F\u002F server\u002Fmain.go\n",[5465,11208,11209,11211],{"class":5467,"line":5475},[5465,11210,9800],{"class":5478},[5465,11212,9803],{"class":5482},[5465,11214,11215],{"class":5467,"line":5493},[5465,11216,5529],{"emptyLinePlaceholder":5528},[5465,11218,11219,11221],{"class":5467,"line":5507},[5465,11220,9812],{"class":5675},[5465,11222,7535],{"class":5489},[5465,11224,11225],{"class":5467,"line":5519},[5465,11226,9829],{"class":5503},[5465,11228,11229],{"class":5467,"line":5525},[5465,11230,11231],{"class":5503},"    \"net\"\n",[5465,11233,11234],{"class":5467,"line":5532},[5465,11235,5597],{"class":5489},[5465,11237,11238,11240],{"class":5467,"line":5565},[5465,11239,9843],{"class":5496},[5465,11241,9846],{"class":5503},[5465,11243,11244],{"class":5467,"line":5594},[5465,11245,11246],{"class":5503},"    \"google.golang.org\u002Fgrpc\"\n",[5465,11248,11249],{"class":5467,"line":5600},[5465,11250,11251],{"class":5503},"    \"google.golang.org\u002Fgrpc\u002Freflection\"\n",[5465,11253,11254],{"class":5467,"line":5624},[5465,11255,5591],{"class":5489},[5465,11257,11258],{"class":5467,"line":5639},[5465,11259,5529],{"emptyLinePlaceholder":5528},[5465,11261,11262,11264,11267],{"class":5467,"line":5647},[5465,11263,5535],{"class":5478},[5465,11265,11266],{"class":5538}," main",[5465,11268,11269],{"class":5489},"() {\n",[5465,11271,11272],{"class":5467,"line":5666},[5465,11273,11274],{"class":5471},"    \u002F\u002F Створюємо TCP-слухач на порту 50051\n",[5465,11276,11277,11280,11282,11284,11286,11289,11291,11294,11296,11299,11301,11304],{"class":5467,"line":5672},[5465,11278,11279],{"class":5496},"    listener",[5465,11281,5336],{"class":5489},[5465,11283,5608],{"class":5496},[5465,11285,5576],{"class":5489},[5465,11287,11288],{"class":5496},"net",[5465,11290,5214],{"class":5489},[5465,11292,11293],{"class":5538},"Listen",[5465,11295,5542],{"class":5489},[5465,11297,11298],{"class":5503},"\"tcp\"",[5465,11300,5336],{"class":5489},[5465,11302,11303],{"class":5503},"\":50051\"",[5465,11305,5591],{"class":5489},[5465,11307,11308,11310,11312,11314,11316],{"class":5467,"line":5690},[5465,11309,5676],{"class":5675},[5465,11311,5679],{"class":5496},[5465,11313,5682],{"class":5489},[5465,11315,5685],{"class":5478},[5465,11317,5490],{"class":5489},[5465,11319,11320,11322,11324,11327,11329,11332,11334,11336,11338,11340],{"class":5467,"line":5699},[5465,11321,11046],{"class":5496},[5465,11323,5214],{"class":5489},[5465,11325,11326],{"class":5538},"Fatalf",[5465,11328,5542],{"class":5489},[5465,11330,11331],{"class":5503},"\"Failed to listen: ",[5465,11333,10887],{"class":5496},[5465,11335,5813],{"class":5503},[5465,11337,5336],{"class":5489},[5465,11339,5608],{"class":5496},[5465,11341,5591],{"class":5489},[5465,11343,11344],{"class":5467,"line":5705},[5465,11345,5702],{"class":5489},[5465,11347,11348],{"class":5467,"line":5727},[5465,11349,5597],{"class":5489},[5465,11351,11352],{"class":5467,"line":5732},[5465,11353,11354],{"class":5471},"    \u002F\u002F Створюємо gRPC-сервер\n",[5465,11356,11357,11360,11362,11364,11366,11369],{"class":5467,"line":5755},[5465,11358,11359],{"class":5496},"    grpcServer",[5465,11361,5576],{"class":5489},[5465,11363,7555],{"class":5496},[5465,11365,5214],{"class":5489},[5465,11367,11368],{"class":5538},"NewServer",[5465,11370,5621],{"class":5489},[5465,11372,11373,11376,11378,11381,11383,11386,11389,11392,11394,11396,11399],{"class":5467,"line":5786},[5465,11374,11375],{"class":5496},"        grpc",[5465,11377,5214],{"class":5489},[5465,11379,11380],{"class":5538},"MaxRecvMsgSize",[5465,11382,5542],{"class":5489},[5465,11384,11385],{"class":7522},"10",[5465,11387,11388],{"class":5489}," * ",[5465,11390,11391],{"class":7522},"1024",[5465,11393,11388],{"class":5489},[5465,11395,11391],{"class":7522},[5465,11397,11398],{"class":5489},"),  ",[5465,11400,11401],{"class":5471},"\u002F\u002F Максимальний розмір вхідного повідомлення: 10 МБ\n",[5465,11403,11404,11406,11408,11411,11413,11415,11417,11419,11421,11423,11425],{"class":5467,"line":5831},[5465,11405,11375],{"class":5496},[5465,11407,5214],{"class":5489},[5465,11409,11410],{"class":5538},"MaxSendMsgSize",[5465,11412,5542],{"class":5489},[5465,11414,11385],{"class":7522},[5465,11416,11388],{"class":5489},[5465,11418,11391],{"class":7522},[5465,11420,11388],{"class":5489},[5465,11422,11391],{"class":7522},[5465,11424,11398],{"class":5489},[5465,11426,11427],{"class":5471},"\u002F\u002F Максимальний розмір вихідного повідомлення: 10 МБ\n",[5465,11429,11430],{"class":5467,"line":5836},[5465,11431,5669],{"class":5489},[5465,11433,11434],{"class":5467,"line":5841},[5465,11435,5597],{"class":5489},[5465,11437,11438],{"class":5467,"line":5850},[5465,11439,11440],{"class":5471},"    \u002F\u002F Реєструємо наш сервіс\n",[5465,11442,11443,11446,11448,11451],{"class":5467,"line":6213},[5465,11444,11445],{"class":5496},"    userService",[5465,11447,5576],{"class":5489},[5465,11449,11450],{"class":5538},"NewUserServer",[5465,11452,5724],{"class":5489},[5465,11454,11455,11457,11459,11462,11464,11467,11469,11472],{"class":5467,"line":6218},[5465,11456,9843],{"class":5496},[5465,11458,5214],{"class":5489},[5465,11460,11461],{"class":5538},"RegisterUserServiceServer",[5465,11463,5542],{"class":5489},[5465,11465,11466],{"class":5496},"grpcServer",[5465,11468,5336],{"class":5489},[5465,11470,11471],{"class":5496},"userService",[5465,11473,5591],{"class":5489},[5465,11475,11476],{"class":5467,"line":6224},[5465,11477,5597],{"class":5489},[5465,11479,11480],{"class":5467,"line":6230},[5465,11481,11482],{"class":5471},"    \u002F\u002F Увімкнення gRPC Reflection для інструментів типу grpcurl\n",[5465,11484,11485,11488,11490,11493,11495,11497],{"class":5467,"line":6236},[5465,11486,11487],{"class":5496},"    reflection",[5465,11489,5214],{"class":5489},[5465,11491,11492],{"class":5538},"Register",[5465,11494,5542],{"class":5489},[5465,11496,11466],{"class":5496},[5465,11498,5591],{"class":5489},[5465,11500,11501],{"class":5467,"line":6242},[5465,11502,5597],{"class":5489},[5465,11504,11505,11507,11509,11511,11513,11516],{"class":5467,"line":6248},[5465,11506,10534],{"class":5496},[5465,11508,5214],{"class":5489},[5465,11510,11098],{"class":5538},[5465,11512,5542],{"class":5489},[5465,11514,11515],{"class":5503},"\"gRPC server listening on :50051\"",[5465,11517,5591],{"class":5489},[5465,11519,11520],{"class":5467,"line":6254},[5465,11521,5597],{"class":5489},[5465,11523,11524],{"class":5467,"line":6260},[5465,11525,11526],{"class":5471},"    \u002F\u002F Блокуючий виклик: приймаємо з'єднання\n",[5465,11528,11529,11531,11533,11535,11537,11539,11542,11544,11547,11549,11551,11553,11555],{"class":5467,"line":6265},[5465,11530,5676],{"class":5675},[5465,11532,5679],{"class":5496},[5465,11534,5576],{"class":5489},[5465,11536,11466],{"class":5496},[5465,11538,5214],{"class":5489},[5465,11540,11541],{"class":5538},"Serve",[5465,11543,5542],{"class":5489},[5465,11545,11546],{"class":5496},"listener",[5465,11548,10960],{"class":5489},[5465,11550,5608],{"class":5496},[5465,11552,5682],{"class":5489},[5465,11554,5685],{"class":5478},[5465,11556,5490],{"class":5489},[5465,11558,11559,11561,11563,11565,11567,11570,11572,11574,11576,11578],{"class":5467,"line":6270},[5465,11560,11046],{"class":5496},[5465,11562,5214],{"class":5489},[5465,11564,11326],{"class":5538},[5465,11566,5542],{"class":5489},[5465,11568,11569],{"class":5503},"\"Failed to serve: ",[5465,11571,10887],{"class":5496},[5465,11573,5813],{"class":5503},[5465,11575,5336],{"class":5489},[5465,11577,5608],{"class":5496},[5465,11579,5591],{"class":5489},[5465,11581,11582],{"class":5467,"line":6276},[5465,11583,5702],{"class":5489},[5465,11585,11586],{"class":5467,"line":6282},[5465,11587,5522],{"class":5489},[5280,11589,11590],{},[5232,11591,10035],{},[5280,11593,11594,11597],{},[5232,11595,11596],{},"Рядки 21–24:"," Створення gRPC-сервера із налаштуваннями максимального розміру повідомлень. За замовчуванням ліміт складає 4 МБ, що може бути недостатньо для файлів або великих списків.",[5280,11599,11600,11603,11604,11607,11608,11610,11611,11613,11614,5214],{},[5232,11601,11602],{},"Рядок 28:"," Функція ",[5211,11605,11606],{},"pb.RegisterUserServiceServer"," згенерована компілятором ",[5211,11609,6435],{}," та реєструє наш ",[5211,11612,9969],{}," як обробник для всіх RPC-методів ",[5211,11615,8985],{},[5280,11617,11618,11621,11622,11625,11626,11628],{},[5232,11619,11620],{},"Рядок 31:"," gRPC Reflection — це стандартний механізм, що дозволяє клієнтським інструментам (наприклад, ",[5211,11623,11624],{},"grpcurl",", Postman, BloomRPC) автоматично виявляти доступні сервіси та методи без доступу до ",[5211,11627,5213],{}," файлів. Обов'язково вмикайте у development-середовищі.",[5280,11630,11631,11164,11634,11637],{},[5232,11632,11633],{},"Рядок 36:",[5211,11635,11636],{},"Serve()"," є блокуючим та приймає вхідні з'єднання у нескінченному циклі, делегуючи кожне з'єднання окремій горутині.",[5267,11639],{},[5270,11641,11643],{"id":11642},"реалізація-grpc-клієнта-у-go","Реалізація gRPC-клієнта у Go",[5275,11645,11647],{"id":11646},"базова-клієнтська-комунікація","Базова клієнтська комунікація",[5280,11649,11650,11651,11654],{},"Згенерований код містить клієнтський stub (",[5284,11652,11653],{},"заглушку","), що інкапсулює всю логіку серіалізації, мережевої комунікації та десеріалізації. Розробнику достатньо викликати методи stub-об'єкта як звичайні локальні функції.",[5456,11656,11658],{"className":5458,"code":11657,"language":5460,"meta":5461,"style":5461},"\u002F\u002F client\u002Fmain.go\npackage main\n\nimport (\n    \"context\"\n    \"io\"\n    \"log\"\n    \"time\"\n    \n    pb \"github.com\u002Fexample\u002Fuserservice\u002Fpb\"\n    \"google.golang.org\u002Fgrpc\"\n    \"google.golang.org\u002Fgrpc\u002Fcredentials\u002Finsecure\"\n)\n\nfunc main() {\n    \u002F\u002F Встановлення з'єднання з gRPC-сервером\n    conn, err := grpc.Dial(\n        \"localhost:50051\",\n        grpc.WithTransportCredentials(insecure.NewCredentials()),  \u002F\u002F Без TLS (тільки для розробки!)\n        grpc.WithBlock(),  \u002F\u002F Блокувати до успішного з'єднання\n    )\n    if err != nil {\n        log.Fatalf(\"Failed to connect: %v\", err)\n    }\n    defer conn.Close()\n    \n    \u002F\u002F Створення клієнтського stub\n    client := pb.NewUserServiceClient(conn)\n    \n    \u002F\u002F Виконуємо виклики RPC-методів\n    demoUnaryRPC(client)\n    demoServerStreamingRPC(client)\n}\n\n\u002F\u002F Демонстрація Unary RPC виклику\nfunc demoUnaryRPC(client pb.UserServiceClient) {\n    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n    defer cancel()\n    \n    log.Println(\"=== Unary RPC: CreateUser ===\")\n    \n    resp, err := client.CreateUser(ctx, &pb.CreateUserRequest{\n        Name:  \"Alice Johnson\",\n        Email: \"alice@example.com\",\n    })\n    if err != nil {\n        log.Fatalf(\"CreateUser failed: %v\", err)\n    }\n    \n    log.Printf(\"Created user: id=%d, name=%s, email=%s\\n\",\n        resp.User.GetId(),\n        resp.User.GetName(),\n        resp.User.GetEmail())\n}\n\n\u002F\u002F Демонстрація Server Streaming RPC виклику\nfunc demoServerStreamingRPC(client pb.UserServiceClient) {\n    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n    defer cancel()\n    \n    log.Println(\"\\n=== Server Streaming RPC: ListUsers ===\")\n    \n    stream, err := client.ListUsers(ctx, &pb.ListUsersRequest{\n        PageSize: 100,\n        Filter:   \"\",\n    })\n    if err != nil {\n        log.Fatalf(\"ListUsers failed: %v\", err)\n    }\n    \n    \u002F\u002F Читаємо повідомлення з потоку до закриття\n    for {\n        user, err := stream.Recv()\n        if err == io.EOF {\n            \u002F\u002F Сервер закрив потік — це нормальне завершення\n            log.Println(\"Stream closed by server\")\n            break\n        }\n        if err != nil {\n            log.Fatalf(\"Failed to receive: %v\", err)\n        }\n        \n        log.Printf(\"Received user: id=%d, name=%s\", user.GetId(), user.GetName())\n    }\n}\n",[5211,11659,11660,11665,11671,11675,11681,11685,11690,11694,11699,11703,11709,11713,11718,11722,11726,11734,11739,11759,11766,11791,11806,11810,11822,11845,11849,11862,11866,11871,11892,11896,11901,11912,11923,11927,11931,11936,11956,12002,12011,12015,12030,12034,12064,12074,12084,12089,12101,12124,12128,12132,12162,12178,12192,12207,12211,12215,12220,12239,12278,12286,12290,12309,12313,12343,12355,12367,12371,12383,12406,12410,12414,12419,12425,12445,12463,12468,12483,12488,12492,12504,12527,12531,12535,12574,12578],{"__ignoreMap":5461},[5465,11661,11662],{"class":5467,"line":5468},[5465,11663,11664],{"class":5471},"\u002F\u002F client\u002Fmain.go\n",[5465,11666,11667,11669],{"class":5467,"line":5475},[5465,11668,9800],{"class":5478},[5465,11670,9803],{"class":5482},[5465,11672,11673],{"class":5467,"line":5493},[5465,11674,5529],{"emptyLinePlaceholder":5528},[5465,11676,11677,11679],{"class":5467,"line":5507},[5465,11678,9812],{"class":5675},[5465,11680,7535],{"class":5489},[5465,11682,11683],{"class":5467,"line":5519},[5465,11684,9819],{"class":5503},[5465,11686,11687],{"class":5467,"line":5525},[5465,11688,11689],{"class":5503},"    \"io\"\n",[5465,11691,11692],{"class":5467,"line":5532},[5465,11693,9829],{"class":5503},[5465,11695,11696],{"class":5467,"line":5565},[5465,11697,11698],{"class":5503},"    \"time\"\n",[5465,11700,11701],{"class":5467,"line":5594},[5465,11702,5597],{"class":5489},[5465,11704,11705,11707],{"class":5467,"line":5600},[5465,11706,9843],{"class":5496},[5465,11708,9846],{"class":5503},[5465,11710,11711],{"class":5467,"line":5624},[5465,11712,11246],{"class":5503},[5465,11714,11715],{"class":5467,"line":5639},[5465,11716,11717],{"class":5503},"    \"google.golang.org\u002Fgrpc\u002Fcredentials\u002Finsecure\"\n",[5465,11719,11720],{"class":5467,"line":5647},[5465,11721,5591],{"class":5489},[5465,11723,11724],{"class":5467,"line":5666},[5465,11725,5529],{"emptyLinePlaceholder":5528},[5465,11727,11728,11730,11732],{"class":5467,"line":5672},[5465,11729,5535],{"class":5478},[5465,11731,11266],{"class":5538},[5465,11733,11269],{"class":5489},[5465,11735,11736],{"class":5467,"line":5690},[5465,11737,11738],{"class":5471},"    \u002F\u002F Встановлення з'єднання з gRPC-сервером\n",[5465,11740,11741,11744,11746,11748,11750,11752,11754,11757],{"class":5467,"line":5699},[5465,11742,11743],{"class":5496},"    conn",[5465,11745,5336],{"class":5489},[5465,11747,5608],{"class":5496},[5465,11749,5576],{"class":5489},[5465,11751,7555],{"class":5496},[5465,11753,5214],{"class":5489},[5465,11755,11756],{"class":5538},"Dial",[5465,11758,5621],{"class":5489},[5465,11760,11761,11764],{"class":5467,"line":5705},[5465,11762,11763],{"class":5503},"        \"localhost:50051\"",[5465,11765,5636],{"class":5489},[5465,11767,11768,11770,11772,11775,11777,11780,11782,11785,11788],{"class":5467,"line":5727},[5465,11769,11375],{"class":5496},[5465,11771,5214],{"class":5489},[5465,11773,11774],{"class":5538},"WithTransportCredentials",[5465,11776,5542],{"class":5489},[5465,11778,11779],{"class":5496},"insecure",[5465,11781,5214],{"class":5489},[5465,11783,11784],{"class":5538},"NewCredentials",[5465,11786,11787],{"class":5489},"()),  ",[5465,11789,11790],{"class":5471},"\u002F\u002F Без TLS (тільки для розробки!)\n",[5465,11792,11793,11795,11797,11800,11803],{"class":5467,"line":5732},[5465,11794,11375],{"class":5496},[5465,11796,5214],{"class":5489},[5465,11798,11799],{"class":5538},"WithBlock",[5465,11801,11802],{"class":5489},"(),  ",[5465,11804,11805],{"class":5471},"\u002F\u002F Блокувати до успішного з'єднання\n",[5465,11807,11808],{"class":5467,"line":5755},[5465,11809,5669],{"class":5489},[5465,11811,11812,11814,11816,11818,11820],{"class":5467,"line":5786},[5465,11813,5676],{"class":5675},[5465,11815,5679],{"class":5496},[5465,11817,5682],{"class":5489},[5465,11819,5685],{"class":5478},[5465,11821,5490],{"class":5489},[5465,11823,11824,11826,11828,11830,11832,11835,11837,11839,11841,11843],{"class":5467,"line":5831},[5465,11825,11046],{"class":5496},[5465,11827,5214],{"class":5489},[5465,11829,11326],{"class":5538},[5465,11831,5542],{"class":5489},[5465,11833,11834],{"class":5503},"\"Failed to connect: ",[5465,11836,10887],{"class":5496},[5465,11838,5813],{"class":5503},[5465,11840,5336],{"class":5489},[5465,11842,5608],{"class":5496},[5465,11844,5591],{"class":5489},[5465,11846,11847],{"class":5467,"line":5836},[5465,11848,5702],{"class":5489},[5465,11850,11851,11853,11856,11858,11860],{"class":5467,"line":5841},[5465,11852,5708],{"class":5675},[5465,11854,11855],{"class":5496}," conn",[5465,11857,5214],{"class":5489},[5465,11859,5721],{"class":5538},[5465,11861,5724],{"class":5489},[5465,11863,11864],{"class":5467,"line":5850},[5465,11865,5597],{"class":5489},[5465,11867,11868],{"class":5467,"line":6213},[5465,11869,11870],{"class":5471},"    \u002F\u002F Створення клієнтського stub\n",[5465,11872,11873,11876,11878,11880,11882,11885,11887,11890],{"class":5467,"line":6218},[5465,11874,11875],{"class":5496},"    client",[5465,11877,5576],{"class":5489},[5465,11879,9928],{"class":5496},[5465,11881,5214],{"class":5489},[5465,11883,11884],{"class":5538},"NewUserServiceClient",[5465,11886,5542],{"class":5489},[5465,11888,11889],{"class":5496},"conn",[5465,11891,5591],{"class":5489},[5465,11893,11894],{"class":5467,"line":6224},[5465,11895,5597],{"class":5489},[5465,11897,11898],{"class":5467,"line":6230},[5465,11899,11900],{"class":5471},"    \u002F\u002F Виконуємо виклики RPC-методів\n",[5465,11902,11903,11906,11908,11910],{"class":5467,"line":6236},[5465,11904,11905],{"class":5538},"    demoUnaryRPC",[5465,11907,5542],{"class":5489},[5465,11909,5878],{"class":5496},[5465,11911,5591],{"class":5489},[5465,11913,11914,11917,11919,11921],{"class":5467,"line":6242},[5465,11915,11916],{"class":5538},"    demoServerStreamingRPC",[5465,11918,5542],{"class":5489},[5465,11920,5878],{"class":5496},[5465,11922,5591],{"class":5489},[5465,11924,11925],{"class":5467,"line":6248},[5465,11926,5522],{"class":5489},[5465,11928,11929],{"class":5467,"line":6254},[5465,11930,5529],{"emptyLinePlaceholder":5528},[5465,11932,11933],{"class":5467,"line":6260},[5465,11934,11935],{"class":5471},"\u002F\u002F Демонстрація Unary RPC виклику\n",[5465,11937,11938,11940,11943,11945,11947,11949,11951,11954],{"class":5467,"line":6265},[5465,11939,5535],{"class":5478},[5465,11941,11942],{"class":5538}," demoUnaryRPC",[5465,11944,5542],{"class":5489},[5465,11946,5878],{"class":5496},[5465,11948,10715],{"class":5482},[5465,11950,5214],{"class":5489},[5465,11952,11953],{"class":5482},"UserServiceClient",[5465,11955,10148],{"class":5489},[5465,11957,11958,11960,11962,11965,11967,11970,11972,11975,11977,11979,11981,11984,11987,11990,11992,11995,11997,12000],{"class":5467,"line":6270},[5465,11959,10104],{"class":5496},[5465,11961,5336],{"class":5489},[5465,11963,11964],{"class":5496},"cancel",[5465,11966,5576],{"class":5489},[5465,11968,11969],{"class":5496},"context",[5465,11971,5214],{"class":5489},[5465,11973,11974],{"class":5538},"WithTimeout",[5465,11976,5542],{"class":5489},[5465,11978,11969],{"class":5496},[5465,11980,5214],{"class":5489},[5465,11982,11983],{"class":5538},"Background",[5465,11985,11986],{"class":5489},"(), ",[5465,11988,11989],{"class":7522},"5",[5465,11991,10091],{"class":5489},[5465,11993,11994],{"class":5496},"time",[5465,11996,5214],{"class":5489},[5465,11998,11999],{"class":5496},"Second",[5465,12001,5591],{"class":5489},[5465,12003,12004,12006,12009],{"class":5467,"line":6276},[5465,12005,5708],{"class":5675},[5465,12007,12008],{"class":5538}," cancel",[5465,12010,5724],{"class":5489},[5465,12012,12013],{"class":5467,"line":6282},[5465,12014,5597],{"class":5489},[5465,12016,12017,12019,12021,12023,12025,12028],{"class":5467,"line":6288},[5465,12018,10534],{"class":5496},[5465,12020,5214],{"class":5489},[5465,12022,11098],{"class":5538},[5465,12024,5542],{"class":5489},[5465,12026,12027],{"class":5503},"\"=== Unary RPC: CreateUser ===\"",[5465,12029,5591],{"class":5489},[5465,12031,12032],{"class":5467,"line":6294},[5465,12033,5597],{"class":5489},[5465,12035,12036,12038,12040,12042,12044,12046,12048,12050,12052,12054,12056,12058,12060,12062],{"class":5467,"line":6300},[5465,12037,5603],{"class":5496},[5465,12039,5336],{"class":5489},[5465,12041,5608],{"class":5496},[5465,12043,5576],{"class":5489},[5465,12045,5878],{"class":5496},[5465,12047,5214],{"class":5489},[5465,12049,5912],{"class":5538},[5465,12051,5542],{"class":5489},[5465,12053,5917],{"class":5496},[5465,12055,5920],{"class":5489},[5465,12057,9928],{"class":5482},[5465,12059,5214],{"class":5489},[5465,12061,9007],{"class":5482},[5465,12063,5926],{"class":5489},[5465,12065,12066,12068,12070,12072],{"class":5467,"line":6306},[5465,12067,10423],{"class":5496},[5465,12069,5933],{"class":5489},[5465,12071,5936],{"class":5503},[5465,12073,5636],{"class":5489},[5465,12075,12076,12078,12080,12082],{"class":5467,"line":6311},[5465,12077,10441],{"class":5496},[5465,12079,5807],{"class":5489},[5465,12081,5947],{"class":5503},[5465,12083,5636],{"class":5489},[5465,12085,12086],{"class":5467,"line":6316},[5465,12087,12088],{"class":5489},"    })\n",[5465,12090,12091,12093,12095,12097,12099],{"class":5467,"line":6322},[5465,12092,5676],{"class":5675},[5465,12094,5679],{"class":5496},[5465,12096,5682],{"class":5489},[5465,12098,5685],{"class":5478},[5465,12100,5490],{"class":5489},[5465,12102,12103,12105,12107,12109,12111,12114,12116,12118,12120,12122],{"class":5467,"line":6328},[5465,12104,11046],{"class":5496},[5465,12106,5214],{"class":5489},[5465,12108,11326],{"class":5538},[5465,12110,5542],{"class":5489},[5465,12112,12113],{"class":5503},"\"CreateUser failed: ",[5465,12115,10887],{"class":5496},[5465,12117,5813],{"class":5503},[5465,12119,5336],{"class":5489},[5465,12121,5608],{"class":5496},[5465,12123,5591],{"class":5489},[5465,12125,12126],{"class":5467,"line":6334},[5465,12127,5702],{"class":5489},[5465,12129,12130],{"class":5467,"line":6340},[5465,12131,5597],{"class":5489},[5465,12133,12134,12136,12138,12140,12142,12144,12146,12148,12150,12153,12155,12158,12160],{"class":5467,"line":6346},[5465,12135,10534],{"class":5496},[5465,12137,5214],{"class":5489},[5465,12139,10539],{"class":5538},[5465,12141,5542],{"class":5489},[5465,12143,10544],{"class":5503},[5465,12145,5804],{"class":5496},[5465,12147,10549],{"class":5503},[5465,12149,5810],{"class":5496},[5465,12151,12152],{"class":5503},", email=",[5465,12154,5810],{"class":5496},[5465,12156,12157],{"class":7881},"\\n",[5465,12159,5813],{"class":5503},[5465,12161,5636],{"class":5489},[5465,12163,12164,12167,12169,12171,12173,12176],{"class":5467,"line":6352},[5465,12165,12166],{"class":5496},"        resp",[5465,12168,5214],{"class":5489},[5465,12170,5923],{"class":5496},[5465,12172,5214],{"class":5489},[5465,12174,12175],{"class":5538},"GetId",[5465,12177,10436],{"class":5489},[5465,12179,12180,12182,12184,12186,12188,12190],{"class":5467,"line":6358},[5465,12181,12166],{"class":5496},[5465,12183,5214],{"class":5489},[5465,12185,5923],{"class":5496},[5465,12187,5214],{"class":5489},[5465,12189,10165],{"class":5538},[5465,12191,10436],{"class":5489},[5465,12193,12194,12196,12198,12200,12202,12204],{"class":5467,"line":6364},[5465,12195,12166],{"class":5496},[5465,12197,5214],{"class":5489},[5465,12199,5923],{"class":5496},[5465,12201,5214],{"class":5489},[5465,12203,10221],{"class":5538},[5465,12205,12206],{"class":5489},"())\n",[5465,12208,12209],{"class":5467,"line":6369},[5465,12210,5522],{"class":5489},[5465,12212,12213],{"class":5467,"line":6374},[5465,12214,5529],{"emptyLinePlaceholder":5528},[5465,12216,12217],{"class":5467,"line":8905},[5465,12218,12219],{"class":5471},"\u002F\u002F Демонстрація Server Streaming RPC виклику\n",[5465,12221,12222,12224,12227,12229,12231,12233,12235,12237],{"class":5467,"line":8910},[5465,12223,5535],{"class":5478},[5465,12225,12226],{"class":5538}," demoServerStreamingRPC",[5465,12228,5542],{"class":5489},[5465,12230,5878],{"class":5496},[5465,12232,10715],{"class":5482},[5465,12234,5214],{"class":5489},[5465,12236,11953],{"class":5482},[5465,12238,10148],{"class":5489},[5465,12240,12241,12243,12245,12247,12249,12251,12253,12255,12257,12259,12261,12263,12265,12268,12270,12272,12274,12276],{"class":5467,"line":8915},[5465,12242,10104],{"class":5496},[5465,12244,5336],{"class":5489},[5465,12246,11964],{"class":5496},[5465,12248,5576],{"class":5489},[5465,12250,11969],{"class":5496},[5465,12252,5214],{"class":5489},[5465,12254,11974],{"class":5538},[5465,12256,5542],{"class":5489},[5465,12258,11969],{"class":5496},[5465,12260,5214],{"class":5489},[5465,12262,11983],{"class":5538},[5465,12264,11986],{"class":5489},[5465,12266,12267],{"class":7522},"30",[5465,12269,10091],{"class":5489},[5465,12271,11994],{"class":5496},[5465,12273,5214],{"class":5489},[5465,12275,11999],{"class":5496},[5465,12277,5591],{"class":5489},[5465,12279,12280,12282,12284],{"class":5467,"line":8921},[5465,12281,5708],{"class":5675},[5465,12283,12008],{"class":5538},[5465,12285,5724],{"class":5489},[5465,12287,12288],{"class":5467,"line":8927},[5465,12289,5597],{"class":5489},[5465,12291,12292,12294,12296,12298,12300,12302,12304,12307],{"class":5467,"line":8933},[5465,12293,10534],{"class":5496},[5465,12295,5214],{"class":5489},[5465,12297,11098],{"class":5538},[5465,12299,5542],{"class":5489},[5465,12301,5813],{"class":5503},[5465,12303,12157],{"class":7881},[5465,12305,12306],{"class":5503},"=== Server Streaming RPC: ListUsers ===\"",[5465,12308,5591],{"class":5489},[5465,12310,12311],{"class":5467,"line":8938},[5465,12312,5597],{"class":5489},[5465,12314,12315,12317,12319,12321,12323,12325,12327,12329,12331,12333,12335,12337,12339,12341],{"class":5467,"line":8943},[5465,12316,10712],{"class":5496},[5465,12318,5336],{"class":5489},[5465,12320,5608],{"class":5496},[5465,12322,5576],{"class":5489},[5465,12324,5878],{"class":5496},[5465,12326,5214],{"class":5489},[5465,12328,10664],{"class":5538},[5465,12330,5542],{"class":5489},[5465,12332,5917],{"class":5496},[5465,12334,5920],{"class":5489},[5465,12336,9928],{"class":5482},[5465,12338,5214],{"class":5489},[5465,12340,10705],{"class":5482},[5465,12342,5926],{"class":5489},[5465,12344,12345,12348,12350,12353],{"class":5467,"line":8949},[5465,12346,12347],{"class":5496},"        PageSize",[5465,12349,5807],{"class":5489},[5465,12351,12352],{"class":7522},"100",[5465,12354,5636],{"class":5489},[5465,12356,12357,12360,12363,12365],{"class":5467,"line":8955},[5465,12358,12359],{"class":5496},"        Filter",[5465,12361,12362],{"class":5489},":   ",[5465,12364,7328],{"class":5503},[5465,12366,5636],{"class":5489},[5465,12368,12369],{"class":5467,"line":8961},[5465,12370,12088],{"class":5489},[5465,12372,12373,12375,12377,12379,12381],{"class":5467,"line":8967},[5465,12374,5676],{"class":5675},[5465,12376,5679],{"class":5496},[5465,12378,5682],{"class":5489},[5465,12380,5685],{"class":5478},[5465,12382,5490],{"class":5489},[5465,12384,12385,12387,12389,12391,12393,12396,12398,12400,12402,12404],{"class":5467,"line":9406},[5465,12386,11046],{"class":5496},[5465,12388,5214],{"class":5489},[5465,12390,11326],{"class":5538},[5465,12392,5542],{"class":5489},[5465,12394,12395],{"class":5503},"\"ListUsers failed: ",[5465,12397,10887],{"class":5496},[5465,12399,5813],{"class":5503},[5465,12401,5336],{"class":5489},[5465,12403,5608],{"class":5496},[5465,12405,5591],{"class":5489},[5465,12407,12408],{"class":5467,"line":9412},[5465,12409,5702],{"class":5489},[5465,12411,12412],{"class":5467,"line":9418},[5465,12413,5597],{"class":5489},[5465,12415,12416],{"class":5467,"line":9423},[5465,12417,12418],{"class":5471},"    \u002F\u002F Читаємо повідомлення з потоку до закриття\n",[5465,12420,12421,12423],{"class":5467,"line":9428},[5465,12422,10813],{"class":5675},[5465,12424,5490],{"class":5489},[5465,12426,12427,12430,12432,12434,12436,12438,12440,12443],{"class":5467,"line":9434},[5465,12428,12429],{"class":5496},"        user",[5465,12431,5336],{"class":5489},[5465,12433,5608],{"class":5496},[5465,12435,5576],{"class":5489},[5465,12437,9028],{"class":5496},[5465,12439,5214],{"class":5489},[5465,12441,12442],{"class":5538},"Recv",[5465,12444,5724],{"class":5489},[5465,12446,12447,12449,12451,12454,12456,12458,12461],{"class":5467,"line":9439},[5465,12448,10843],{"class":5675},[5465,12450,5679],{"class":5496},[5465,12452,12453],{"class":5489}," == ",[5465,12455,5767],{"class":5496},[5465,12457,5214],{"class":5489},[5465,12459,12460],{"class":5496},"EOF",[5465,12462,5490],{"class":5489},[5465,12464,12465],{"class":5467,"line":9445},[5465,12466,12467],{"class":5471},"            \u002F\u002F Сервер закрив потік — це нормальне завершення\n",[5465,12469,12470,12472,12474,12476,12478,12481],{"class":5467,"line":9451},[5465,12471,10875],{"class":5496},[5465,12473,5214],{"class":5489},[5465,12475,11098],{"class":5538},[5465,12477,5542],{"class":5489},[5465,12479,12480],{"class":5503},"\"Stream closed by server\"",[5465,12482,5591],{"class":5489},[5465,12484,12485],{"class":5467,"line":9456},[5465,12486,12487],{"class":5675},"            break\n",[5465,12489,12490],{"class":5467,"line":9462},[5465,12491,10928],{"class":5489},[5465,12493,12494,12496,12498,12500,12502],{"class":5467,"line":9468},[5465,12495,10843],{"class":5675},[5465,12497,5679],{"class":5496},[5465,12499,5682],{"class":5489},[5465,12501,5685],{"class":5478},[5465,12503,5490],{"class":5489},[5465,12505,12506,12508,12510,12512,12514,12517,12519,12521,12523,12525],{"class":5467,"line":9474},[5465,12507,10875],{"class":5496},[5465,12509,5214],{"class":5489},[5465,12511,11326],{"class":5538},[5465,12513,5542],{"class":5489},[5465,12515,12516],{"class":5503},"\"Failed to receive: ",[5465,12518,10887],{"class":5496},[5465,12520,5813],{"class":5503},[5465,12522,5336],{"class":5489},[5465,12524,5608],{"class":5496},[5465,12526,5591],{"class":5489},[5465,12528,12529],{"class":5467,"line":9480},[5465,12530,10928],{"class":5489},[5465,12532,12533],{"class":5467,"line":9486},[5465,12534,10933],{"class":5489},[5465,12536,12537,12539,12541,12543,12545,12548,12550,12552,12554,12556,12558,12560,12562,12564,12566,12568,12570,12572],{"class":5467,"line":9492},[5465,12538,11046],{"class":5496},[5465,12540,5214],{"class":5489},[5465,12542,10539],{"class":5538},[5465,12544,5542],{"class":5489},[5465,12546,12547],{"class":5503},"\"Received user: id=",[5465,12549,5804],{"class":5496},[5465,12551,10549],{"class":5503},[5465,12553,5810],{"class":5496},[5465,12555,5813],{"class":5503},[5465,12557,5336],{"class":5489},[5465,12559,5552],{"class":5496},[5465,12561,5214],{"class":5489},[5465,12563,12175],{"class":5538},[5465,12565,11986],{"class":5489},[5465,12567,5552],{"class":5496},[5465,12569,5214],{"class":5489},[5465,12571,10165],{"class":5538},[5465,12573,12206],{"class":5489},[5465,12575,12576],{"class":5467,"line":9498},[5465,12577,5702],{"class":5489},[5465,12579,12580],{"class":5467,"line":9504},[5465,12581,5522],{"class":5489},[5280,12583,12584],{},[5232,12585,12586],{},"Аналіз клієнтського коду:",[5280,12588,12589,11164,12592,12595,12596,12599,12600,12603],{},[5232,12590,12591],{},"Рядки 17–20:",[5211,12593,12594],{},"grpc.Dial()"," встановлює з'єднання з сервером. Опція ",[5211,12597,12598],{},"grpc.WithTransportCredentials(insecure.NewCredentials())"," вимикає TLS — ",[5232,12601,12602],{},"це безпечно лише для локальної розробки",". У продакшн-середовищі обов'язково використовуйте TLS-сертифікати.",[5280,12605,12606,6758,12609,12612,12613,12616,12617,12619],{},[5232,12607,12608],{},"Рядок 19:",[5211,12610,12611],{},"grpc.WithBlock()"," робить ",[5211,12614,12615],{},"Dial()"," блокуючим до успішного встановлення з'єднання. Без цієї опції ",[5211,12618,12615],{}," повертається негайно, а з'єднання встановлюється асинхронно у фоні. Це корисно для швидкого старту, але ускладнює обробку помилок конфігурації.",[5280,12621,12622,11603,12624,12627,12628,12631],{},[5232,12623,11602],{},[5211,12625,12626],{},"pb.NewUserServiceClient(conn)"," створює клієнтський stub, що реалізує інтерфейс ",[5211,12629,12630],{},"pb.UserServiceClient"," зі всіма методами сервісу.",[5280,12633,12634,6758,12637,12640],{},[5232,12635,12636],{},"Рядки 37–38:",[5232,12638,12639],{},"Завжди створюйте контекст із таймаутом для gRPC-викликів."," Без таймауту клієнт може чекати нескінченно, якщо сервер завис або мережа нестабільна. Типові таймаути: 1–5 секунд для простих запитів, 30–60 секунд для складних обчислень.",[5280,12642,12643,12646,12647,12650],{},[5232,12644,12645],{},"Рядки 42–45:"," Виклик ",[5211,12648,12649],{},"client.CreateUser()"," виглядає як звичайний виклик локальної функції. gRPC автоматично:",[6403,12652,12653,12659,12662,12665],{},[5201,12654,12655,12656,12658],{},"Серіалізує ",[5211,12657,9007],{}," у Protobuf.",[5201,12660,12661],{},"Надсилає HTTP\u002F2 HEADERS та DATA фрейми через з'єднання.",[5201,12663,12664],{},"Чекає на відповідь від сервера.",[5201,12666,12667,12668,5214],{},"Десеріалізує відповідь у структуру ",[5211,12669,9017],{},[5280,12671,12672,12675,12676,5321,12679,12681,12682,5214],{},[5232,12673,12674],{},"Рядки 64–68:"," Виклик Server Streaming RPC методу повертає не результат, а ",[5232,12677,12678],{},"об'єкт потоку",[5211,12680,9028],{},"), що реалізує інтерфейс ",[5211,12683,12684],{},"pb.UserService_ListUsersClient",[5280,12686,12687,12690,12691,12694,12695,12698,12699,12702],{},[5232,12688,12689],{},"Рядки 71–82:"," Читаємо повідомлення з потоку у циклі через метод ",[5211,12692,12693],{},"stream.Recv()",". Коли сервер закриває потік, ",[5211,12696,12697],{},"Recv()"," повертає помилку ",[5211,12700,12701],{},"io.EOF",", що є сигналом нормального завершення (а не справжньої помилки).",[7043,12704,12705,12706,12708,12709,12712,12713,12715],{},"Помилка ",[5211,12707,12701],{}," у контексті gRPC Streaming означає ",[5232,12710,12711],{},"успішне завершення потоку",", а не помилку читання. Завжди обробляйте ",[5211,12714,12701],{}," окремо від інших помилок. Будь-яка інша помилка означає мережеву проблему або помилку на сервері та вимагає логування та повторної спроби.",[5275,12717,12719],{"id":12718},"обробка-помилок-та-статус-коди-grpc","Обробка помилок та статус-коди gRPC",[5280,12721,12722,12723,12726],{},"gRPC використовує власну систему статус-кодів, визначених у пакеті ",[5211,12724,12725],{},"google.golang.org\u002Fgrpc\u002Fcodes",". Ці коди аналогічні до HTTP статус-кодів, але оптимізовані для RPC-комунікації.",[5280,12728,12729],{},[5232,12730,12731],{},"Таблиця основних gRPC статус-кодів:",[6855,12733,12734,12752],{},[6858,12735,12736],{},[6861,12737,12738,12741,12744,12747,12749],{},[6864,12739,12740],{},"Код",[6864,12742,12743],{},"Назва",[6864,12745,12746],{},"HTTP еквівалент",[6864,12748,6872],{},[6864,12750,12751],{},"Коли використовувати",[6877,12753,12754,12774,12794,12815,12836,12857,12877,12898,12919,12940,12960,12979,12999,13020,13040,13061,13081],{},[6861,12755,12756,12760,12765,12768,12771],{},[6882,12757,12758],{},[5211,12759,6820],{},[6882,12761,12762],{},[5211,12763,12764],{},"OK",[6882,12766,12767],{},"200 OK",[6882,12769,12770],{},"Успішне виконання",[6882,12772,12773],{},"Нормальне завершення",[6861,12775,12776,12780,12785,12788,12791],{},[6882,12777,12778],{},[5211,12779,10020],{},[6882,12781,12782],{},[5211,12783,12784],{},"CANCELLED",[6882,12786,12787],{},"499 Client Closed Request",[6882,12789,12790],{},"Клієнт скасував запит",[6882,12792,12793],{},"Контекст закрито клієнтом",[6861,12795,12796,12801,12806,12809,12812],{},[6882,12797,12798],{},[5211,12799,12800],{},"2",[6882,12802,12803],{},[5211,12804,12805],{},"UNKNOWN",[6882,12807,12808],{},"500 Internal Server Error",[6882,12810,12811],{},"Невідома помилка",[6882,12813,12814],{},"Несподівані exception",[6861,12816,12817,12822,12827,12830,12833],{},[6882,12818,12819],{},[5211,12820,12821],{},"3",[6882,12823,12824],{},[5211,12825,12826],{},"INVALID_ARGUMENT",[6882,12828,12829],{},"400 Bad Request",[6882,12831,12832],{},"Некоректні параметри",[6882,12834,12835],{},"Валідація не пройшла",[6861,12837,12838,12843,12848,12851,12854],{},[6882,12839,12840],{},[5211,12841,12842],{},"4",[6882,12844,12845],{},[5211,12846,12847],{},"DEADLINE_EXCEEDED",[6882,12849,12850],{},"504 Gateway Timeout",[6882,12852,12853],{},"Перевищено таймаут",[6882,12855,12856],{},"Запит виконувався занадто довго",[6861,12858,12859,12863,12868,12871,12874],{},[6882,12860,12861],{},[5211,12862,11989],{},[6882,12864,12865],{},[5211,12866,12867],{},"NOT_FOUND",[6882,12869,12870],{},"404 Not Found",[6882,12872,12873],{},"Ресурс не знайдено",[6882,12875,12876],{},"Користувач\u002Fзапис відсутній",[6861,12878,12879,12884,12889,12892,12895],{},[6882,12880,12881],{},[5211,12882,12883],{},"6",[6882,12885,12886],{},[5211,12887,12888],{},"ALREADY_EXISTS",[6882,12890,12891],{},"409 Conflict",[6882,12893,12894],{},"Ресурс вже існує",[6882,12896,12897],{},"Дублікат email, унікальне обмеження",[6861,12899,12900,12905,12910,12913,12916],{},[6882,12901,12902],{},[5211,12903,12904],{},"7",[6882,12906,12907],{},[5211,12908,12909],{},"PERMISSION_DENIED",[6882,12911,12912],{},"403 Forbidden",[6882,12914,12915],{},"Доступ заборонено",[6882,12917,12918],{},"Недостатньо прав",[6861,12920,12921,12926,12931,12934,12937],{},[6882,12922,12923],{},[5211,12924,12925],{},"8",[6882,12927,12928],{},[5211,12929,12930],{},"RESOURCE_EXHAUSTED",[6882,12932,12933],{},"429 Too Many Requests",[6882,12935,12936],{},"Вичерпано ресурси",[6882,12938,12939],{},"Rate limit, квота перевищена",[6861,12941,12942,12947,12952,12954,12957],{},[6882,12943,12944],{},[5211,12945,12946],{},"9",[6882,12948,12949],{},[5211,12950,12951],{},"FAILED_PRECONDITION",[6882,12953,12829],{},[6882,12955,12956],{},"Передумова не виконана",[6882,12958,12959],{},"Некоректний стан об'єкта",[6861,12961,12962,12966,12971,12973,12976],{},[6882,12963,12964],{},[5211,12965,11385],{},[6882,12967,12968],{},[5211,12969,12970],{},"ABORTED",[6882,12972,12891],{},[6882,12974,12975],{},"Операція перервана",[6882,12977,12978],{},"Конкурентна модифікація",[6861,12980,12981,12986,12991,12993,12996],{},[6882,12982,12983],{},[5211,12984,12985],{},"11",[6882,12987,12988],{},[5211,12989,12990],{},"OUT_OF_RANGE",[6882,12992,12829],{},[6882,12994,12995],{},"Параметр поза діапазоном",[6882,12997,12998],{},"Некоректна пагінація",[6861,13000,13001,13006,13011,13014,13017],{},[6882,13002,13003],{},[5211,13004,13005],{},"12",[6882,13007,13008],{},[5211,13009,13010],{},"UNIMPLEMENTED",[6882,13012,13013],{},"501 Not Implemented",[6882,13015,13016],{},"Метод не реалізовано",[6882,13018,13019],{},"RPC метод відсутній",[6861,13021,13022,13027,13032,13034,13037],{},[6882,13023,13024],{},[5211,13025,13026],{},"13",[6882,13028,13029],{},[5211,13030,13031],{},"INTERNAL",[6882,13033,12808],{},[6882,13035,13036],{},"Внутрішня помилка",[6882,13038,13039],{},"Падіння БД, panic",[6861,13041,13042,13047,13052,13055,13058],{},[6882,13043,13044],{},[5211,13045,13046],{},"14",[6882,13048,13049],{},[5211,13050,13051],{},"UNAVAILABLE",[6882,13053,13054],{},"503 Service Unavailable",[6882,13056,13057],{},"Сервіс недоступний",[6882,13059,13060],{},"Сервер перевантажений",[6861,13062,13063,13068,13073,13075,13078],{},[6882,13064,13065],{},[5211,13066,13067],{},"15",[6882,13069,13070],{},[5211,13071,13072],{},"DATA_LOSS",[6882,13074,12808],{},[6882,13076,13077],{},"Втрата даних",[6882,13079,13080],{},"Пошкодження файлів",[6861,13082,13083,13088,13093,13096,13099],{},[6882,13084,13085],{},[5211,13086,13087],{},"16",[6882,13089,13090],{},[5211,13091,13092],{},"UNAUTHENTICATED",[6882,13094,13095],{},"401 Unauthorized",[6882,13097,13098],{},"Не автентифіковано",[6882,13100,13101],{},"Відсутній або некоректний токен",[5280,13103,13104],{},[5232,13105,13106],{},"Приклад обробки статус-кодів на клієнті:",[5456,13108,13110],{"className":5458,"code":13109,"language":5460,"meta":5461,"style":5461},"import (\n    \"google.golang.org\u002Fgrpc\u002Fcodes\"\n    \"google.golang.org\u002Fgrpc\u002Fstatus\"\n)\n\nfunc createUserWithErrorHandling(client pb.UserServiceClient, name, email string) {\n    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n    defer cancel()\n    \n    resp, err := client.CreateUser(ctx, &pb.CreateUserRequest{\n        Name:  name,\n        Email: email,\n    })\n    \n    if err != nil {\n        \u002F\u002F Перетворюємо помилку на gRPC статус\n        st, ok := status.FromError(err)\n        if !ok {\n            \u002F\u002F Не gRPC помилка (наприклад, мережева помилка)\n            log.Fatalf(\"Non-gRPC error: %v\", err)\n        }\n        \n        \u002F\u002F Обробляємо різні типи помилок\n        switch st.Code() {\n        case codes.InvalidArgument:\n            log.Printf(\"Validation error: %s\", st.Message())\n        case codes.AlreadyExists:\n            log.Printf(\"User already exists: %s\", st.Message())\n        case codes.DeadlineExceeded:\n            log.Println(\"Request timeout, retry later\")\n        case codes.Unavailable:\n            log.Println(\"Service unavailable, retry with backoff\")\n        default:\n            log.Printf(\"RPC failed: code=%s, message=%s\", st.Code(), st.Message())\n        }\n        return\n    }\n    \n    log.Printf(\"Success: user created with id=%d\", resp.User.GetId())\n}\n",[5211,13111,13112,13118,13122,13126,13130,13134,13163,13201,13209,13213,13243,13253,13263,13267,13271,13283,13288,13313,13324,13329,13352,13356,13360,13365,13380,13394,13423,13436,13463,13475,13490,13503,13518,13525,13565,13569,13574,13578,13582,13613],{"__ignoreMap":5461},[5465,13113,13114,13116],{"class":5467,"line":5468},[5465,13115,9812],{"class":5675},[5465,13117,7535],{"class":5489},[5465,13119,13120],{"class":5467,"line":5475},[5465,13121,9851],{"class":5503},[5465,13123,13124],{"class":5467,"line":5493},[5465,13125,9856],{"class":5503},[5465,13127,13128],{"class":5467,"line":5507},[5465,13129,5591],{"class":5489},[5465,13131,13132],{"class":5467,"line":5519},[5465,13133,5529],{"emptyLinePlaceholder":5528},[5465,13135,13136,13138,13141,13143,13145,13147,13149,13151,13153,13155,13157,13159,13161],{"class":5467,"line":5525},[5465,13137,5535],{"class":5478},[5465,13139,13140],{"class":5538}," createUserWithErrorHandling",[5465,13142,5542],{"class":5489},[5465,13144,5878],{"class":5496},[5465,13146,10715],{"class":5482},[5465,13148,5214],{"class":5489},[5465,13150,11953],{"class":5482},[5465,13152,5336],{"class":5489},[5465,13154,6779],{"class":5496},[5465,13156,5336],{"class":5489},[5465,13158,6782],{"class":5496},[5465,13160,5513],{"class":5482},[5465,13162,10148],{"class":5489},[5465,13164,13165,13167,13169,13171,13173,13175,13177,13179,13181,13183,13185,13187,13189,13191,13193,13195,13197,13199],{"class":5467,"line":5532},[5465,13166,10104],{"class":5496},[5465,13168,5336],{"class":5489},[5465,13170,11964],{"class":5496},[5465,13172,5576],{"class":5489},[5465,13174,11969],{"class":5496},[5465,13176,5214],{"class":5489},[5465,13178,11974],{"class":5538},[5465,13180,5542],{"class":5489},[5465,13182,11969],{"class":5496},[5465,13184,5214],{"class":5489},[5465,13186,11983],{"class":5538},[5465,13188,11986],{"class":5489},[5465,13190,11989],{"class":7522},[5465,13192,10091],{"class":5489},[5465,13194,11994],{"class":5496},[5465,13196,5214],{"class":5489},[5465,13198,11999],{"class":5496},[5465,13200,5591],{"class":5489},[5465,13202,13203,13205,13207],{"class":5467,"line":5565},[5465,13204,5708],{"class":5675},[5465,13206,12008],{"class":5538},[5465,13208,5724],{"class":5489},[5465,13210,13211],{"class":5467,"line":5594},[5465,13212,5597],{"class":5489},[5465,13214,13215,13217,13219,13221,13223,13225,13227,13229,13231,13233,13235,13237,13239,13241],{"class":5467,"line":5600},[5465,13216,5603],{"class":5496},[5465,13218,5336],{"class":5489},[5465,13220,5608],{"class":5496},[5465,13222,5576],{"class":5489},[5465,13224,5878],{"class":5496},[5465,13226,5214],{"class":5489},[5465,13228,5912],{"class":5538},[5465,13230,5542],{"class":5489},[5465,13232,5917],{"class":5496},[5465,13234,5920],{"class":5489},[5465,13236,9928],{"class":5482},[5465,13238,5214],{"class":5489},[5465,13240,9007],{"class":5482},[5465,13242,5926],{"class":5489},[5465,13244,13245,13247,13249,13251],{"class":5467,"line":5624},[5465,13246,10423],{"class":5496},[5465,13248,5933],{"class":5489},[5465,13250,6779],{"class":5496},[5465,13252,5636],{"class":5489},[5465,13254,13255,13257,13259,13261],{"class":5467,"line":5639},[5465,13256,10441],{"class":5496},[5465,13258,5807],{"class":5489},[5465,13260,6782],{"class":5496},[5465,13262,5636],{"class":5489},[5465,13264,13265],{"class":5467,"line":5647},[5465,13266,12088],{"class":5489},[5465,13268,13269],{"class":5467,"line":5666},[5465,13270,5597],{"class":5489},[5465,13272,13273,13275,13277,13279,13281],{"class":5467,"line":5672},[5465,13274,5676],{"class":5675},[5465,13276,5679],{"class":5496},[5465,13278,5682],{"class":5489},[5465,13280,5685],{"class":5478},[5465,13282,5490],{"class":5489},[5465,13284,13285],{"class":5467,"line":5690},[5465,13286,13287],{"class":5471},"        \u002F\u002F Перетворюємо помилку на gRPC статус\n",[5465,13289,13290,13293,13295,13298,13300,13302,13304,13307,13309,13311],{"class":5467,"line":5699},[5465,13291,13292],{"class":5496},"        st",[5465,13294,5336],{"class":5489},[5465,13296,13297],{"class":5496},"ok",[5465,13299,5576],{"class":5489},[5465,13301,10184],{"class":5496},[5465,13303,5214],{"class":5489},[5465,13305,13306],{"class":5538},"FromError",[5465,13308,5542],{"class":5489},[5465,13310,5608],{"class":5496},[5465,13312,5591],{"class":5489},[5465,13314,13315,13317,13320,13322],{"class":5467,"line":5705},[5465,13316,10843],{"class":5675},[5465,13318,13319],{"class":5489}," !",[5465,13321,13297],{"class":5496},[5465,13323,5490],{"class":5489},[5465,13325,13326],{"class":5467,"line":5727},[5465,13327,13328],{"class":5471},"            \u002F\u002F Не gRPC помилка (наприклад, мережева помилка)\n",[5465,13330,13331,13333,13335,13337,13339,13342,13344,13346,13348,13350],{"class":5467,"line":5732},[5465,13332,10875],{"class":5496},[5465,13334,5214],{"class":5489},[5465,13336,11326],{"class":5538},[5465,13338,5542],{"class":5489},[5465,13340,13341],{"class":5503},"\"Non-gRPC error: ",[5465,13343,10887],{"class":5496},[5465,13345,5813],{"class":5503},[5465,13347,5336],{"class":5489},[5465,13349,5608],{"class":5496},[5465,13351,5591],{"class":5489},[5465,13353,13354],{"class":5467,"line":5755},[5465,13355,10928],{"class":5489},[5465,13357,13358],{"class":5467,"line":5786},[5465,13359,10933],{"class":5489},[5465,13361,13362],{"class":5467,"line":5831},[5465,13363,13364],{"class":5471},"        \u002F\u002F Обробляємо різні типи помилок\n",[5465,13366,13367,13370,13373,13375,13378],{"class":5467,"line":5836},[5465,13368,13369],{"class":5675},"        switch",[5465,13371,13372],{"class":5496}," st",[5465,13374,5214],{"class":5489},[5465,13376,13377],{"class":5538},"Code",[5465,13379,11269],{"class":5489},[5465,13381,13382,13385,13388,13390,13392],{"class":5467,"line":5841},[5465,13383,13384],{"class":5675},"        case",[5465,13386,13387],{"class":5496}," codes",[5465,13389,5214],{"class":5489},[5465,13391,10199],{"class":5496},[5465,13393,10332],{"class":5489},[5465,13395,13396,13398,13400,13402,13404,13407,13409,13411,13413,13416,13418,13421],{"class":5467,"line":5850},[5465,13397,10875],{"class":5496},[5465,13399,5214],{"class":5489},[5465,13401,10539],{"class":5538},[5465,13403,5542],{"class":5489},[5465,13405,13406],{"class":5503},"\"Validation error: ",[5465,13408,5810],{"class":5496},[5465,13410,5813],{"class":5503},[5465,13412,5336],{"class":5489},[5465,13414,13415],{"class":5496},"st",[5465,13417,5214],{"class":5489},[5465,13419,13420],{"class":5538},"Message",[5465,13422,12206],{"class":5489},[5465,13424,13425,13427,13429,13431,13434],{"class":5467,"line":6213},[5465,13426,13384],{"class":5675},[5465,13428,13387],{"class":5496},[5465,13430,5214],{"class":5489},[5465,13432,13433],{"class":5496},"AlreadyExists",[5465,13435,10332],{"class":5489},[5465,13437,13438,13440,13442,13444,13446,13449,13451,13453,13455,13457,13459,13461],{"class":5467,"line":6218},[5465,13439,10875],{"class":5496},[5465,13441,5214],{"class":5489},[5465,13443,10539],{"class":5538},[5465,13445,5542],{"class":5489},[5465,13447,13448],{"class":5503},"\"User already exists: ",[5465,13450,5810],{"class":5496},[5465,13452,5813],{"class":5503},[5465,13454,5336],{"class":5489},[5465,13456,13415],{"class":5496},[5465,13458,5214],{"class":5489},[5465,13460,13420],{"class":5538},[5465,13462,12206],{"class":5489},[5465,13464,13465,13467,13469,13471,13473],{"class":5467,"line":6224},[5465,13466,13384],{"class":5675},[5465,13468,13387],{"class":5496},[5465,13470,5214],{"class":5489},[5465,13472,10317],{"class":5496},[5465,13474,10332],{"class":5489},[5465,13476,13477,13479,13481,13483,13485,13488],{"class":5467,"line":6230},[5465,13478,10875],{"class":5496},[5465,13480,5214],{"class":5489},[5465,13482,11098],{"class":5538},[5465,13484,5542],{"class":5489},[5465,13486,13487],{"class":5503},"\"Request timeout, retry later\"",[5465,13489,5591],{"class":5489},[5465,13491,13492,13494,13496,13498,13501],{"class":5467,"line":6236},[5465,13493,13384],{"class":5675},[5465,13495,13387],{"class":5496},[5465,13497,5214],{"class":5489},[5465,13499,13500],{"class":5496},"Unavailable",[5465,13502,10332],{"class":5489},[5465,13504,13505,13507,13509,13511,13513,13516],{"class":5467,"line":6242},[5465,13506,10875],{"class":5496},[5465,13508,5214],{"class":5489},[5465,13510,11098],{"class":5538},[5465,13512,5542],{"class":5489},[5465,13514,13515],{"class":5503},"\"Service unavailable, retry with backoff\"",[5465,13517,5591],{"class":5489},[5465,13519,13520,13523],{"class":5467,"line":6248},[5465,13521,13522],{"class":5675},"        default",[5465,13524,10332],{"class":5489},[5465,13526,13527,13529,13531,13533,13535,13538,13540,13543,13545,13547,13549,13551,13553,13555,13557,13559,13561,13563],{"class":5467,"line":6254},[5465,13528,10875],{"class":5496},[5465,13530,5214],{"class":5489},[5465,13532,10539],{"class":5538},[5465,13534,5542],{"class":5489},[5465,13536,13537],{"class":5503},"\"RPC failed: code=",[5465,13539,5810],{"class":5496},[5465,13541,13542],{"class":5503},", message=",[5465,13544,5810],{"class":5496},[5465,13546,5813],{"class":5503},[5465,13548,5336],{"class":5489},[5465,13550,13415],{"class":5496},[5465,13552,5214],{"class":5489},[5465,13554,13377],{"class":5538},[5465,13556,11986],{"class":5489},[5465,13558,13415],{"class":5496},[5465,13560,5214],{"class":5489},[5465,13562,13420],{"class":5538},[5465,13564,12206],{"class":5489},[5465,13566,13567],{"class":5467,"line":6260},[5465,13568,10928],{"class":5489},[5465,13570,13571],{"class":5467,"line":6265},[5465,13572,13573],{"class":5675},"        return\n",[5465,13575,13576],{"class":5467,"line":6270},[5465,13577,5702],{"class":5489},[5465,13579,13580],{"class":5467,"line":6276},[5465,13581,5597],{"class":5489},[5465,13583,13584,13586,13588,13590,13592,13595,13597,13599,13601,13603,13605,13607,13609,13611],{"class":5467,"line":6282},[5465,13585,10534],{"class":5496},[5465,13587,5214],{"class":5489},[5465,13589,10539],{"class":5538},[5465,13591,5542],{"class":5489},[5465,13593,13594],{"class":5503},"\"Success: user created with id=",[5465,13596,5804],{"class":5496},[5465,13598,5813],{"class":5503},[5465,13600,5336],{"class":5489},[5465,13602,5777],{"class":5496},[5465,13604,5214],{"class":5489},[5465,13606,5923],{"class":5496},[5465,13608,5214],{"class":5489},[5465,13610,12175],{"class":5538},[5465,13612,12206],{"class":5489},[5465,13614,13615],{"class":5467,"line":6288},[5465,13616,5522],{"class":5489},[6839,13618,13619,13620,13622,13623,13626,13627,13630,13631,5336,13633,5336,13636,5214],{},"Використовуйте спеціалізовані бібліотеки для автоматичних повторних спроб (",[5284,13621,9685],{},") із експоненційним затуханням (",[5284,13624,13625],{},"exponential backoff","). Пакет ",[5211,13628,13629],{},"github.com\u002Fgrpc-ecosystem\u002Fgo-grpc-middleware\u002Fretry"," надає готовий інтерцептор, що автоматично повторює запити при помилках ",[5211,13632,13500],{},[5211,13634,13635],{},"ResourceExhausted",[5211,13637,10317],{},[5267,13639],{},[5270,13641,13643],{"id":13642},"grpc-інтерцептори-middleware-для-rpc-викликів","gRPC Інтерцептори: middleware для RPC-викликів",[5275,13645,13647],{"id":13646},"концепція-інтерцепторів","Концепція інтерцепторів",[5280,13649,13650,5321,13653,13656],{},[5232,13651,13652],{},"Інтерцептори",[5284,13654,13655],{},"interceptors",") у gRPC аналогічні до middleware у вебфреймворках (Gin, Chi) — це функції, що перехоплюють RPC-виклики до їх виконання (Unary) або під час обробки потоку (Streaming). Інтерцептори дозволяють додавати наскрізну функціональність без модифікації бізнес-логіки кожного методу:",[5198,13658,13659,13662,13665,13668,13671,13674],{},[5201,13660,13661],{},"Автентифікація та авторизація (перевірка JWT токенів).",[5201,13663,13664],{},"Логування запитів та відповідей.",[5201,13666,13667],{},"Збір метрик (тривалість виклику, кількість помилок).",[5201,13669,13670],{},"Трейсинг (розподілене відстеження запитів через мікросервіси).",[5201,13672,13673],{},"Rate Limiting (обмеження частоти запитів).",[5201,13675,13676],{},"Валідація вхідних даних.",[5280,13678,13679,13680,8158],{},"gRPC розрізняє ",[5232,13681,13682],{},"два типи інтерцепторів",[6403,13684,13685,13691],{},[5201,13686,13687,13690],{},[5232,13688,13689],{},"Unary Interceptor"," — обробляє Unary RPC виклики (один запит → одна відповідь).",[5201,13692,13693,13696],{},[5232,13694,13695],{},"Stream Interceptor"," — обробляє Streaming RPC виклики (потокові операції).",[5275,13698,13700],{"id":13699},"реалізація-unary-interceptor-для-логування","Реалізація Unary Interceptor для логування",[5280,13702,13703],{},"Unary Interceptor має сигнатуру функції, що обгортає виклик RPC-методу:",[5456,13705,13707],{"className":5458,"code":13706,"language":5460,"meta":5461,"style":5461},"\u002F\u002F server\u002Finterceptors\u002Flogging.go\npackage interceptors\n\nimport (\n    \"context\"\n    \"log\"\n    \"time\"\n    \n    \"google.golang.org\u002Fgrpc\"\n    \"google.golang.org\u002Fgrpc\u002Fcodes\"\n    \"google.golang.org\u002Fgrpc\u002Fstatus\"\n)\n\n\u002F\u002F UnaryLoggingInterceptor логує всі Unary RPC виклики\nfunc UnaryLoggingInterceptor(\n    ctx context.Context,\n    req interface{},\n    info *grpc.UnaryServerInfo,\n    handler grpc.UnaryHandler,\n) (interface{}, error) {\n    start := time.Now()\n    \n    log.Printf(\"[gRPC] → Method: %s\", info.FullMethod)\n    \n    \u002F\u002F Викликаємо оригінальний обробник\n    resp, err := handler(ctx, req)\n    \n    \u002F\u002F Логуємо результат\n    duration := time.Since(start)\n    \n    if err != nil {\n        st, _ := status.FromError(err)\n        log.Printf(\"[gRPC] ← Method: %s, Status: %s, Duration: %v, Error: %s\",\n            info.FullMethod,\n            st.Code(),\n            duration,\n            st.Message())\n    } else {\n        log.Printf(\"[gRPC] ← Method: %s, Status: OK, Duration: %v\",\n            info.FullMethod,\n            duration)\n    }\n    \n    return resp, err\n}\n",[5211,13708,13709,13714,13721,13725,13731,13735,13739,13743,13747,13751,13755,13759,13763,13767,13772,13781,13793,13803,13819,13834,13849,13865,13869,13898,13902,13907,13930,13934,13939,13960,13964,13976,13998,14032,14043,14054,14061,14071,14081,14104,14114,14120,14124,14128,14139],{"__ignoreMap":5461},[5465,13710,13711],{"class":5467,"line":5468},[5465,13712,13713],{"class":5471},"\u002F\u002F server\u002Finterceptors\u002Flogging.go\n",[5465,13715,13716,13718],{"class":5467,"line":5475},[5465,13717,9800],{"class":5478},[5465,13719,13720],{"class":5482}," interceptors\n",[5465,13722,13723],{"class":5467,"line":5493},[5465,13724,5529],{"emptyLinePlaceholder":5528},[5465,13726,13727,13729],{"class":5467,"line":5507},[5465,13728,9812],{"class":5675},[5465,13730,7535],{"class":5489},[5465,13732,13733],{"class":5467,"line":5519},[5465,13734,9819],{"class":5503},[5465,13736,13737],{"class":5467,"line":5525},[5465,13738,9829],{"class":5503},[5465,13740,13741],{"class":5467,"line":5532},[5465,13742,11698],{"class":5503},[5465,13744,13745],{"class":5467,"line":5565},[5465,13746,5597],{"class":5489},[5465,13748,13749],{"class":5467,"line":5594},[5465,13750,11246],{"class":5503},[5465,13752,13753],{"class":5467,"line":5600},[5465,13754,9851],{"class":5503},[5465,13756,13757],{"class":5467,"line":5624},[5465,13758,9856],{"class":5503},[5465,13760,13761],{"class":5467,"line":5639},[5465,13762,5591],{"class":5489},[5465,13764,13765],{"class":5467,"line":5647},[5465,13766,5529],{"emptyLinePlaceholder":5528},[5465,13768,13769],{"class":5467,"line":5666},[5465,13770,13771],{"class":5471},"\u002F\u002F UnaryLoggingInterceptor логує всі Unary RPC виклики\n",[5465,13773,13774,13776,13779],{"class":5467,"line":5672},[5465,13775,5535],{"class":5478},[5465,13777,13778],{"class":5538}," UnaryLoggingInterceptor",[5465,13780,5621],{"class":5489},[5465,13782,13783,13785,13787,13789,13791],{"class":5467,"line":5690},[5465,13784,10104],{"class":5496},[5465,13786,10107],{"class":5482},[5465,13788,5214],{"class":5489},[5465,13790,10112],{"class":5482},[5465,13792,5636],{"class":5489},[5465,13794,13795,13797,13800],{"class":5467,"line":5699},[5465,13796,10119],{"class":5496},[5465,13798,13799],{"class":5478}," interface",[5465,13801,13802],{"class":5489},"{},\n",[5465,13804,13805,13808,13810,13812,13814,13817],{"class":5467,"line":5705},[5465,13806,13807],{"class":5496},"    info",[5465,13809,10122],{"class":5489},[5465,13811,7555],{"class":5482},[5465,13813,5214],{"class":5489},[5465,13815,13816],{"class":5482},"UnaryServerInfo",[5465,13818,5636],{"class":5489},[5465,13820,13821,13824,13827,13829,13832],{"class":5467,"line":5727},[5465,13822,13823],{"class":5496},"    handler",[5465,13825,13826],{"class":5482}," grpc",[5465,13828,5214],{"class":5489},[5465,13830,13831],{"class":5482},"UnaryHandler",[5465,13833,5636],{"class":5489},[5465,13835,13836,13839,13842,13845,13847],{"class":5467,"line":5732},[5465,13837,13838],{"class":5489},") (",[5465,13840,13841],{"class":5478},"interface",[5465,13843,13844],{"class":5489},"{}, ",[5465,13846,5560],{"class":5482},[5465,13848,10148],{"class":5489},[5465,13850,13851,13854,13856,13858,13860,13863],{"class":5467,"line":5755},[5465,13852,13853],{"class":5496},"    start",[5465,13855,5576],{"class":5489},[5465,13857,11994],{"class":5496},[5465,13859,5214],{"class":5489},[5465,13861,13862],{"class":5538},"Now",[5465,13864,5724],{"class":5489},[5465,13866,13867],{"class":5467,"line":5786},[5465,13868,5597],{"class":5489},[5465,13870,13871,13873,13875,13877,13879,13882,13884,13886,13888,13891,13893,13896],{"class":5467,"line":5831},[5465,13872,10534],{"class":5496},[5465,13874,5214],{"class":5489},[5465,13876,10539],{"class":5538},[5465,13878,5542],{"class":5489},[5465,13880,13881],{"class":5503},"\"[gRPC] → Method: ",[5465,13883,5810],{"class":5496},[5465,13885,5813],{"class":5503},[5465,13887,5336],{"class":5489},[5465,13889,13890],{"class":5496},"info",[5465,13892,5214],{"class":5489},[5465,13894,13895],{"class":5496},"FullMethod",[5465,13897,5591],{"class":5489},[5465,13899,13900],{"class":5467,"line":5836},[5465,13901,5597],{"class":5489},[5465,13903,13904],{"class":5467,"line":5841},[5465,13905,13906],{"class":5471},"    \u002F\u002F Викликаємо оригінальний обробник\n",[5465,13908,13909,13911,13913,13915,13917,13920,13922,13924,13926,13928],{"class":5467,"line":5850},[5465,13910,5603],{"class":5496},[5465,13912,5336],{"class":5489},[5465,13914,5608],{"class":5496},[5465,13916,5576],{"class":5489},[5465,13918,13919],{"class":5538},"handler",[5465,13921,5542],{"class":5489},[5465,13923,5917],{"class":5496},[5465,13925,5336],{"class":5489},[5465,13927,10429],{"class":5496},[5465,13929,5591],{"class":5489},[5465,13931,13932],{"class":5467,"line":6213},[5465,13933,5597],{"class":5489},[5465,13935,13936],{"class":5467,"line":6218},[5465,13937,13938],{"class":5471},"    \u002F\u002F Логуємо результат\n",[5465,13940,13941,13944,13946,13948,13950,13953,13955,13958],{"class":5467,"line":6224},[5465,13942,13943],{"class":5496},"    duration",[5465,13945,5576],{"class":5489},[5465,13947,11994],{"class":5496},[5465,13949,5214],{"class":5489},[5465,13951,13952],{"class":5538},"Since",[5465,13954,5542],{"class":5489},[5465,13956,13957],{"class":5496},"start",[5465,13959,5591],{"class":5489},[5465,13961,13962],{"class":5467,"line":6230},[5465,13963,5597],{"class":5489},[5465,13965,13966,13968,13970,13972,13974],{"class":5467,"line":6236},[5465,13967,5676],{"class":5675},[5465,13969,5679],{"class":5496},[5465,13971,5682],{"class":5489},[5465,13973,5685],{"class":5478},[5465,13975,5490],{"class":5489},[5465,13977,13978,13980,13982,13984,13986,13988,13990,13992,13994,13996],{"class":5467,"line":6242},[5465,13979,13292],{"class":5496},[5465,13981,5336],{"class":5489},[5465,13983,5573],{"class":5496},[5465,13985,5576],{"class":5489},[5465,13987,10184],{"class":5496},[5465,13989,5214],{"class":5489},[5465,13991,13306],{"class":5538},[5465,13993,5542],{"class":5489},[5465,13995,5608],{"class":5496},[5465,13997,5591],{"class":5489},[5465,13999,14000,14002,14004,14006,14008,14011,14013,14016,14018,14021,14023,14026,14028,14030],{"class":5467,"line":6248},[5465,14001,11046],{"class":5496},[5465,14003,5214],{"class":5489},[5465,14005,10539],{"class":5538},[5465,14007,5542],{"class":5489},[5465,14009,14010],{"class":5503},"\"[gRPC] ← Method: ",[5465,14012,5810],{"class":5496},[5465,14014,14015],{"class":5503},", Status: ",[5465,14017,5810],{"class":5496},[5465,14019,14020],{"class":5503},", Duration: ",[5465,14022,10887],{"class":5496},[5465,14024,14025],{"class":5503},", Error: ",[5465,14027,5810],{"class":5496},[5465,14029,5813],{"class":5503},[5465,14031,5636],{"class":5489},[5465,14033,14034,14037,14039,14041],{"class":5467,"line":6254},[5465,14035,14036],{"class":5496},"            info",[5465,14038,5214],{"class":5489},[5465,14040,13895],{"class":5496},[5465,14042,5636],{"class":5489},[5465,14044,14045,14048,14050,14052],{"class":5467,"line":6260},[5465,14046,14047],{"class":5496},"            st",[5465,14049,5214],{"class":5489},[5465,14051,13377],{"class":5538},[5465,14053,10436],{"class":5489},[5465,14055,14056,14059],{"class":5467,"line":6265},[5465,14057,14058],{"class":5496},"            duration",[5465,14060,5636],{"class":5489},[5465,14062,14063,14065,14067,14069],{"class":5467,"line":6270},[5465,14064,14047],{"class":5496},[5465,14066,5214],{"class":5489},[5465,14068,13420],{"class":5538},[5465,14070,12206],{"class":5489},[5465,14072,14073,14076,14079],{"class":5467,"line":6276},[5465,14074,14075],{"class":5489},"    } ",[5465,14077,14078],{"class":5675},"else",[5465,14080,5490],{"class":5489},[5465,14082,14083,14085,14087,14089,14091,14093,14095,14098,14100,14102],{"class":5467,"line":6282},[5465,14084,11046],{"class":5496},[5465,14086,5214],{"class":5489},[5465,14088,10539],{"class":5538},[5465,14090,5542],{"class":5489},[5465,14092,14010],{"class":5503},[5465,14094,5810],{"class":5496},[5465,14096,14097],{"class":5503},", Status: OK, Duration: ",[5465,14099,10887],{"class":5496},[5465,14101,5813],{"class":5503},[5465,14103,5636],{"class":5489},[5465,14105,14106,14108,14110,14112],{"class":5467,"line":6288},[5465,14107,14036],{"class":5496},[5465,14109,5214],{"class":5489},[5465,14111,13895],{"class":5496},[5465,14113,5636],{"class":5489},[5465,14115,14116,14118],{"class":5467,"line":6294},[5465,14117,14058],{"class":5496},[5465,14119,5591],{"class":5489},[5465,14121,14122],{"class":5467,"line":6300},[5465,14123,5702],{"class":5489},[5465,14125,14126],{"class":5467,"line":6306},[5465,14127,5597],{"class":5489},[5465,14129,14130,14132,14134,14136],{"class":5467,"line":6311},[5465,14131,5844],{"class":5675},[5465,14133,5711],{"class":5496},[5465,14135,5336],{"class":5489},[5465,14137,14138],{"class":5496},"err\n",[5465,14140,14141],{"class":5467,"line":6316},[5465,14142,5522],{"class":5489},[5280,14144,14145],{},[5232,14146,10035],{},[5280,14148,14149,14155],{},[5232,14150,14151,14152,8158],{},"Параметр ",[5211,14153,14154],{},"ctx context.Context"," Контекст запиту, що містить метадані, таймаути та сигнали скасування.",[5280,14157,14158,14163,14164,14167,14168,14171],{},[5232,14159,14151,14160,8158],{},[5211,14161,14162],{},"req interface{}"," Десеріалізований запит (наприклад, ",[5211,14165,14166],{},"*pb.CreateUserRequest","). Використовується тип ",[5211,14169,14170],{},"interface{}",", оскільки інтерцептор є універсальним для всіх методів.",[5280,14173,14174,14179,14180,14182,14183,14186,14187,14190],{},[5232,14175,14151,14176,8158],{},[5211,14177,14178],{},"info *grpc.UnaryServerInfo"," Містить метаінформацію про виклик: ",[5211,14181,13895],{}," (повна назва методу, наприклад ",[5211,14184,14185],{},"\u002Fuserservice.UserService\u002FCreateUser","), ",[5211,14188,14189],{},"Server"," (покажчик на gRPC-сервер).",[5280,14192,14193,14198,14199,14202,14203,5336,14205,14207],{},[5232,14194,14151,14195,8158],{},[5211,14196,14197],{},"handler grpc.UnaryHandler"," Функція, що викликає оригінальний обробник RPC-методу. Виклик ",[5211,14200,14201],{},"handler(ctx, req)"," передає керування бізнес-логіці (",[5211,14204,5912],{},[5211,14206,8998],{}," тощо).",[5280,14209,14210,14213],{},[5232,14211,14212],{},"Рядок 26:"," Викликаємо оригінальний обробник та зберігаємо результат та помилку. Це дозволяє вимірювати час виконання методу.",[5280,14215,14216,14219,14220,14222],{},[5232,14217,14218],{},"Рядки 31–41:"," Логуємо результат виклику: успішний (статус ",[5211,14221,12764],{},") або з помилкою (статус-код та повідомлення).",[5275,14224,14226],{"id":14225},"реалізація-unary-interceptor-для-автентифікації-jwt","Реалізація Unary Interceptor для автентифікації JWT",[5280,14228,14229],{},"Інтерцептор автентифікації перевіряє наявність та валідність JWT токена у метаданих запиту перед викликом бізнес-логіки.",[5456,14231,14233],{"className":5458,"code":14232,"language":5460,"meta":5461,"style":5461},"\u002F\u002F server\u002Finterceptors\u002Fauth.go\npackage interceptors\n\nimport (\n    \"context\"\n    \"strings\"\n    \n    \"google.golang.org\u002Fgrpc\"\n    \"google.golang.org\u002Fgrpc\u002Fcodes\"\n    \"google.golang.org\u002Fgrpc\u002Fmetadata\"\n    \"google.golang.org\u002Fgrpc\u002Fstatus\"\n)\n\n\u002F\u002F UnaryAuthInterceptor перевіряє JWT токен у метаданих\nfunc UnaryAuthInterceptor(\n    ctx context.Context,\n    req interface{},\n    info *grpc.UnaryServerInfo,\n    handler grpc.UnaryHandler,\n) (interface{}, error) {\n    \u002F\u002F Список публічних методів, що не вимагають автентифікації\n    publicMethods := map[string]bool{\n        \"\u002Fuserservice.UserService\u002FCreateUser\": true,  \u002F\u002F Реєстрація публічна\n    }\n    \n    \u002F\u002F Якщо метод публічний, пропускаємо перевірку\n    if publicMethods[info.FullMethod] {\n        return handler(ctx, req)\n    }\n    \n    \u002F\u002F Отримуємо метадані з контексту\n    md, ok := metadata.FromIncomingContext(ctx)\n    if !ok {\n        return nil, status.Error(codes.Unauthenticated, \"missing metadata\")\n    }\n    \n    \u002F\u002F Шукаємо заголовок Authorization\n    authHeaders := md.Get(\"authorization\")\n    if len(authHeaders) == 0 {\n        return nil, status.Error(codes.Unauthenticated, \"missing authorization header\")\n    }\n    \n    \u002F\u002F Формат: \"Bearer \u003Ctoken>\"\n    token := strings.TrimPrefix(authHeaders[0], \"Bearer \")\n    if token == authHeaders[0] {\n        return nil, status.Error(codes.Unauthenticated, \"invalid authorization format\")\n    }\n    \n    \u002F\u002F Валідація JWT токена (спрощена версія)\n    userID, err := validateJWT(token)\n    if err != nil {\n        return nil, status.Error(codes.Unauthenticated, \"invalid token\")\n    }\n    \n    \u002F\u002F Додаємо userID до контексту для використання у обробниках\n    ctx = context.WithValue(ctx, \"user_id\", userID)\n    \n    \u002F\u002F Викликаємо оригінальний обробник із збагаченим контекстом\n    return handler(ctx, req)\n}\n\n\u002F\u002F validateJWT перевіряє JWT токен та повертає user ID (заглушка)\nfunc validateJWT(token string) (int64, error) {\n    \u002F\u002F У реальній системі тут має бути перевірка підпису JWT,\n    \u002F\u002F терміну дії та витягування claims\n    if token == \"valid_token_123\" {\n        return 42, nil\n    }\n    return 0, status.Error(codes.Unauthenticated, \"invalid token\")\n}\n",[5211,14234,14235,14240,14246,14250,14256,14260,14265,14269,14273,14277,14282,14286,14290,14294,14299,14308,14320,14328,14342,14354,14366,14371,14391,14407,14411,14415,14420,14438,14455,14459,14463,14468,14493,14503,14533,14537,14541,14546,14568,14587,14616,14620,14624,14629,14660,14677,14706,14710,14714,14719,14740,14752,14781,14785,14789,14794,14824,14828,14833,14849,14853,14857,14862,14885,14890,14895,14908,14919,14923,14952],{"__ignoreMap":5461},[5465,14236,14237],{"class":5467,"line":5468},[5465,14238,14239],{"class":5471},"\u002F\u002F server\u002Finterceptors\u002Fauth.go\n",[5465,14241,14242,14244],{"class":5467,"line":5475},[5465,14243,9800],{"class":5478},[5465,14245,13720],{"class":5482},[5465,14247,14248],{"class":5467,"line":5493},[5465,14249,5529],{"emptyLinePlaceholder":5528},[5465,14251,14252,14254],{"class":5467,"line":5507},[5465,14253,9812],{"class":5675},[5465,14255,7535],{"class":5489},[5465,14257,14258],{"class":5467,"line":5519},[5465,14259,9819],{"class":5503},[5465,14261,14262],{"class":5467,"line":5525},[5465,14263,14264],{"class":5503},"    \"strings\"\n",[5465,14266,14267],{"class":5467,"line":5532},[5465,14268,5597],{"class":5489},[5465,14270,14271],{"class":5467,"line":5565},[5465,14272,11246],{"class":5503},[5465,14274,14275],{"class":5467,"line":5594},[5465,14276,9851],{"class":5503},[5465,14278,14279],{"class":5467,"line":5600},[5465,14280,14281],{"class":5503},"    \"google.golang.org\u002Fgrpc\u002Fmetadata\"\n",[5465,14283,14284],{"class":5467,"line":5624},[5465,14285,9856],{"class":5503},[5465,14287,14288],{"class":5467,"line":5639},[5465,14289,5591],{"class":5489},[5465,14291,14292],{"class":5467,"line":5647},[5465,14293,5529],{"emptyLinePlaceholder":5528},[5465,14295,14296],{"class":5467,"line":5666},[5465,14297,14298],{"class":5471},"\u002F\u002F UnaryAuthInterceptor перевіряє JWT токен у метаданих\n",[5465,14300,14301,14303,14306],{"class":5467,"line":5672},[5465,14302,5535],{"class":5478},[5465,14304,14305],{"class":5538}," UnaryAuthInterceptor",[5465,14307,5621],{"class":5489},[5465,14309,14310,14312,14314,14316,14318],{"class":5467,"line":5690},[5465,14311,10104],{"class":5496},[5465,14313,10107],{"class":5482},[5465,14315,5214],{"class":5489},[5465,14317,10112],{"class":5482},[5465,14319,5636],{"class":5489},[5465,14321,14322,14324,14326],{"class":5467,"line":5699},[5465,14323,10119],{"class":5496},[5465,14325,13799],{"class":5478},[5465,14327,13802],{"class":5489},[5465,14329,14330,14332,14334,14336,14338,14340],{"class":5467,"line":5705},[5465,14331,13807],{"class":5496},[5465,14333,10122],{"class":5489},[5465,14335,7555],{"class":5482},[5465,14337,5214],{"class":5489},[5465,14339,13816],{"class":5482},[5465,14341,5636],{"class":5489},[5465,14343,14344,14346,14348,14350,14352],{"class":5467,"line":5727},[5465,14345,13823],{"class":5496},[5465,14347,13826],{"class":5482},[5465,14349,5214],{"class":5489},[5465,14351,13831],{"class":5482},[5465,14353,5636],{"class":5489},[5465,14355,14356,14358,14360,14362,14364],{"class":5467,"line":5732},[5465,14357,13838],{"class":5489},[5465,14359,13841],{"class":5478},[5465,14361,13844],{"class":5489},[5465,14363,5560],{"class":5482},[5465,14365,10148],{"class":5489},[5465,14367,14368],{"class":5467,"line":5755},[5465,14369,14370],{"class":5471},"    \u002F\u002F Список публічних методів, що не вимагають автентифікації\n",[5465,14372,14373,14376,14378,14380,14382,14384,14387,14389],{"class":5467,"line":5786},[5465,14374,14375],{"class":5496},"    publicMethods",[5465,14377,5576],{"class":5489},[5465,14379,7078],{"class":5478},[5465,14381,9920],{"class":5489},[5465,14383,6764],{"class":5482},[5465,14385,14386],{"class":5489},"]",[5465,14388,6973],{"class":5482},[5465,14390,5926],{"class":5489},[5465,14392,14393,14396,14398,14401,14404],{"class":5467,"line":5831},[5465,14394,14395],{"class":5503},"        \"\u002Fuserservice.UserService\u002FCreateUser\"",[5465,14397,5807],{"class":5489},[5465,14399,14400],{"class":5478},"true",[5465,14402,14403],{"class":5489},",  ",[5465,14405,14406],{"class":5471},"\u002F\u002F Реєстрація публічна\n",[5465,14408,14409],{"class":5467,"line":5836},[5465,14410,5702],{"class":5489},[5465,14412,14413],{"class":5467,"line":5841},[5465,14414,5597],{"class":5489},[5465,14416,14417],{"class":5467,"line":5850},[5465,14418,14419],{"class":5471},"    \u002F\u002F Якщо метод публічний, пропускаємо перевірку\n",[5465,14421,14422,14424,14427,14429,14431,14433,14435],{"class":5467,"line":6213},[5465,14423,5676],{"class":5675},[5465,14425,14426],{"class":5496}," publicMethods",[5465,14428,9920],{"class":5489},[5465,14430,13890],{"class":5496},[5465,14432,5214],{"class":5489},[5465,14434,13895],{"class":5496},[5465,14436,14437],{"class":5489},"] {\n",[5465,14439,14440,14442,14445,14447,14449,14451,14453],{"class":5467,"line":6218},[5465,14441,5693],{"class":5675},[5465,14443,14444],{"class":5538}," handler",[5465,14446,5542],{"class":5489},[5465,14448,5917],{"class":5496},[5465,14450,5336],{"class":5489},[5465,14452,10429],{"class":5496},[5465,14454,5591],{"class":5489},[5465,14456,14457],{"class":5467,"line":6224},[5465,14458,5702],{"class":5489},[5465,14460,14461],{"class":5467,"line":6230},[5465,14462,5597],{"class":5489},[5465,14464,14465],{"class":5467,"line":6236},[5465,14466,14467],{"class":5471},"    \u002F\u002F Отримуємо метадані з контексту\n",[5465,14469,14470,14473,14475,14477,14479,14482,14484,14487,14489,14491],{"class":5467,"line":6242},[5465,14471,14472],{"class":5496},"    md",[5465,14474,5336],{"class":5489},[5465,14476,13297],{"class":5496},[5465,14478,5576],{"class":5489},[5465,14480,14481],{"class":5496},"metadata",[5465,14483,5214],{"class":5489},[5465,14485,14486],{"class":5538},"FromIncomingContext",[5465,14488,5542],{"class":5489},[5465,14490,5917],{"class":5496},[5465,14492,5591],{"class":5489},[5465,14494,14495,14497,14499,14501],{"class":5467,"line":6248},[5465,14496,5676],{"class":5675},[5465,14498,13319],{"class":5489},[5465,14500,13297],{"class":5496},[5465,14502,5490],{"class":5489},[5465,14504,14505,14507,14509,14511,14513,14515,14517,14519,14521,14523,14526,14528,14531],{"class":5467,"line":6254},[5465,14506,5693],{"class":5675},[5465,14508,10179],{"class":5478},[5465,14510,5336],{"class":5489},[5465,14512,10184],{"class":5496},[5465,14514,5214],{"class":5489},[5465,14516,10189],{"class":5538},[5465,14518,5542],{"class":5489},[5465,14520,10194],{"class":5496},[5465,14522,5214],{"class":5489},[5465,14524,14525],{"class":5496},"Unauthenticated",[5465,14527,5336],{"class":5489},[5465,14529,14530],{"class":5503},"\"missing metadata\"",[5465,14532,5591],{"class":5489},[5465,14534,14535],{"class":5467,"line":6260},[5465,14536,5702],{"class":5489},[5465,14538,14539],{"class":5467,"line":6265},[5465,14540,5597],{"class":5489},[5465,14542,14543],{"class":5467,"line":6270},[5465,14544,14545],{"class":5471},"    \u002F\u002F Шукаємо заголовок Authorization\n",[5465,14547,14548,14551,14553,14556,14558,14561,14563,14566],{"class":5467,"line":6276},[5465,14549,14550],{"class":5496},"    authHeaders",[5465,14552,5576],{"class":5489},[5465,14554,14555],{"class":5496},"md",[5465,14557,5214],{"class":5489},[5465,14559,14560],{"class":5538},"Get",[5465,14562,5542],{"class":5489},[5465,14564,14565],{"class":5503},"\"authorization\"",[5465,14567,5591],{"class":5489},[5465,14569,14570,14572,14575,14577,14580,14583,14585],{"class":5467,"line":6282},[5465,14571,5676],{"class":5675},[5465,14573,14574],{"class":5538}," len",[5465,14576,5542],{"class":5489},[5465,14578,14579],{"class":5496},"authHeaders",[5465,14581,14582],{"class":5489},") == ",[5465,14584,6820],{"class":7522},[5465,14586,5490],{"class":5489},[5465,14588,14589,14591,14593,14595,14597,14599,14601,14603,14605,14607,14609,14611,14614],{"class":5467,"line":6288},[5465,14590,5693],{"class":5675},[5465,14592,10179],{"class":5478},[5465,14594,5336],{"class":5489},[5465,14596,10184],{"class":5496},[5465,14598,5214],{"class":5489},[5465,14600,10189],{"class":5538},[5465,14602,5542],{"class":5489},[5465,14604,10194],{"class":5496},[5465,14606,5214],{"class":5489},[5465,14608,14525],{"class":5496},[5465,14610,5336],{"class":5489},[5465,14612,14613],{"class":5503},"\"missing authorization header\"",[5465,14615,5591],{"class":5489},[5465,14617,14618],{"class":5467,"line":6294},[5465,14619,5702],{"class":5489},[5465,14621,14622],{"class":5467,"line":6300},[5465,14623,5597],{"class":5489},[5465,14625,14626],{"class":5467,"line":6306},[5465,14627,14628],{"class":5471},"    \u002F\u002F Формат: \"Bearer \u003Ctoken>\"\n",[5465,14630,14631,14634,14636,14639,14641,14644,14646,14648,14650,14652,14655,14658],{"class":5467,"line":6311},[5465,14632,14633],{"class":5496},"    token",[5465,14635,5576],{"class":5489},[5465,14637,14638],{"class":5496},"strings",[5465,14640,5214],{"class":5489},[5465,14642,14643],{"class":5538},"TrimPrefix",[5465,14645,5542],{"class":5489},[5465,14647,14579],{"class":5496},[5465,14649,9920],{"class":5489},[5465,14651,6820],{"class":7522},[5465,14653,14654],{"class":5489},"], ",[5465,14656,14657],{"class":5503},"\"Bearer \"",[5465,14659,5591],{"class":5489},[5465,14661,14662,14664,14667,14669,14671,14673,14675],{"class":5467,"line":6316},[5465,14663,5676],{"class":5675},[5465,14665,14666],{"class":5496}," token",[5465,14668,12453],{"class":5489},[5465,14670,14579],{"class":5496},[5465,14672,9920],{"class":5489},[5465,14674,6820],{"class":7522},[5465,14676,14437],{"class":5489},[5465,14678,14679,14681,14683,14685,14687,14689,14691,14693,14695,14697,14699,14701,14704],{"class":5467,"line":6322},[5465,14680,5693],{"class":5675},[5465,14682,10179],{"class":5478},[5465,14684,5336],{"class":5489},[5465,14686,10184],{"class":5496},[5465,14688,5214],{"class":5489},[5465,14690,10189],{"class":5538},[5465,14692,5542],{"class":5489},[5465,14694,10194],{"class":5496},[5465,14696,5214],{"class":5489},[5465,14698,14525],{"class":5496},[5465,14700,5336],{"class":5489},[5465,14702,14703],{"class":5503},"\"invalid authorization format\"",[5465,14705,5591],{"class":5489},[5465,14707,14708],{"class":5467,"line":6328},[5465,14709,5702],{"class":5489},[5465,14711,14712],{"class":5467,"line":6334},[5465,14713,5597],{"class":5489},[5465,14715,14716],{"class":5467,"line":6340},[5465,14717,14718],{"class":5471},"    \u002F\u002F Валідація JWT токена (спрощена версія)\n",[5465,14720,14721,14724,14726,14728,14730,14733,14735,14738],{"class":5467,"line":6346},[5465,14722,14723],{"class":5496},"    userID",[5465,14725,5336],{"class":5489},[5465,14727,5608],{"class":5496},[5465,14729,5576],{"class":5489},[5465,14731,14732],{"class":5538},"validateJWT",[5465,14734,5542],{"class":5489},[5465,14736,14737],{"class":5496},"token",[5465,14739,5591],{"class":5489},[5465,14741,14742,14744,14746,14748,14750],{"class":5467,"line":6352},[5465,14743,5676],{"class":5675},[5465,14745,5679],{"class":5496},[5465,14747,5682],{"class":5489},[5465,14749,5685],{"class":5478},[5465,14751,5490],{"class":5489},[5465,14753,14754,14756,14758,14760,14762,14764,14766,14768,14770,14772,14774,14776,14779],{"class":5467,"line":6358},[5465,14755,5693],{"class":5675},[5465,14757,10179],{"class":5478},[5465,14759,5336],{"class":5489},[5465,14761,10184],{"class":5496},[5465,14763,5214],{"class":5489},[5465,14765,10189],{"class":5538},[5465,14767,5542],{"class":5489},[5465,14769,10194],{"class":5496},[5465,14771,5214],{"class":5489},[5465,14773,14525],{"class":5496},[5465,14775,5336],{"class":5489},[5465,14777,14778],{"class":5503},"\"invalid token\"",[5465,14780,5591],{"class":5489},[5465,14782,14783],{"class":5467,"line":6364},[5465,14784,5702],{"class":5489},[5465,14786,14787],{"class":5467,"line":6369},[5465,14788,5597],{"class":5489},[5465,14790,14791],{"class":5467,"line":6374},[5465,14792,14793],{"class":5471},"    \u002F\u002F Додаємо userID до контексту для використання у обробниках\n",[5465,14795,14796,14798,14801,14803,14805,14808,14810,14812,14814,14817,14819,14822],{"class":5467,"line":8905},[5465,14797,10104],{"class":5496},[5465,14799,14800],{"class":5489}," = ",[5465,14802,11969],{"class":5496},[5465,14804,5214],{"class":5489},[5465,14806,14807],{"class":5538},"WithValue",[5465,14809,5542],{"class":5489},[5465,14811,5917],{"class":5496},[5465,14813,5336],{"class":5489},[5465,14815,14816],{"class":5503},"\"user_id\"",[5465,14818,5336],{"class":5489},[5465,14820,14821],{"class":5496},"userID",[5465,14823,5591],{"class":5489},[5465,14825,14826],{"class":5467,"line":8910},[5465,14827,5597],{"class":5489},[5465,14829,14830],{"class":5467,"line":8915},[5465,14831,14832],{"class":5471},"    \u002F\u002F Викликаємо оригінальний обробник із збагаченим контекстом\n",[5465,14834,14835,14837,14839,14841,14843,14845,14847],{"class":5467,"line":8921},[5465,14836,5844],{"class":5675},[5465,14838,14444],{"class":5538},[5465,14840,5542],{"class":5489},[5465,14842,5917],{"class":5496},[5465,14844,5336],{"class":5489},[5465,14846,10429],{"class":5496},[5465,14848,5591],{"class":5489},[5465,14850,14851],{"class":5467,"line":8927},[5465,14852,5522],{"class":5489},[5465,14854,14855],{"class":5467,"line":8933},[5465,14856,5529],{"emptyLinePlaceholder":5528},[5465,14858,14859],{"class":5467,"line":8938},[5465,14860,14861],{"class":5471},"\u002F\u002F validateJWT перевіряє JWT токен та повертає user ID (заглушка)\n",[5465,14863,14864,14866,14869,14871,14873,14875,14877,14879,14881,14883],{"class":5467,"line":8943},[5465,14865,5535],{"class":5478},[5465,14867,14868],{"class":5538}," validateJWT",[5465,14870,5542],{"class":5489},[5465,14872,14737],{"class":5496},[5465,14874,5513],{"class":5482},[5465,14876,13838],{"class":5489},[5465,14878,6761],{"class":5482},[5465,14880,5336],{"class":5489},[5465,14882,5560],{"class":5482},[5465,14884,10148],{"class":5489},[5465,14886,14887],{"class":5467,"line":8949},[5465,14888,14889],{"class":5471},"    \u002F\u002F У реальній системі тут має бути перевірка підпису JWT,\n",[5465,14891,14892],{"class":5467,"line":8955},[5465,14893,14894],{"class":5471},"    \u002F\u002F терміну дії та витягування claims\n",[5465,14896,14897,14899,14901,14903,14906],{"class":5467,"line":8961},[5465,14898,5676],{"class":5675},[5465,14900,14666],{"class":5496},[5465,14902,12453],{"class":5489},[5465,14904,14905],{"class":5503},"\"valid_token_123\"",[5465,14907,5490],{"class":5489},[5465,14909,14910,14912,14915,14917],{"class":5467,"line":8967},[5465,14911,5693],{"class":5675},[5465,14913,14914],{"class":7522}," 42",[5465,14916,5336],{"class":5489},[5465,14918,10604],{"class":5478},[5465,14920,14921],{"class":5467,"line":9406},[5465,14922,5702],{"class":5489},[5465,14924,14925,14927,14930,14932,14934,14936,14938,14940,14942,14944,14946,14948,14950],{"class":5467,"line":9412},[5465,14926,5844],{"class":5675},[5465,14928,14929],{"class":7522}," 0",[5465,14931,5336],{"class":5489},[5465,14933,10184],{"class":5496},[5465,14935,5214],{"class":5489},[5465,14937,10189],{"class":5538},[5465,14939,5542],{"class":5489},[5465,14941,10194],{"class":5496},[5465,14943,5214],{"class":5489},[5465,14945,14525],{"class":5496},[5465,14947,5336],{"class":5489},[5465,14949,14778],{"class":5503},[5465,14951,5591],{"class":5489},[5465,14953,14954],{"class":5467,"line":9418},[5465,14955,5522],{"class":5489},[5280,14957,14958],{},[5232,14959,14960],{},"Ключові моменти:",[5280,14962,14963,14966,14967,5336,14969,5336,14971,14973],{},[5232,14964,14965],{},"Рядки 22–29:"," Визначаємо список публічних методів, що доступні без автентифікації (реєстрація, вхід). Для захищених методів (",[5211,14968,8998],{},[5211,14970,9065],{},[5211,14972,9068],{},") виконується перевірка токена.",[5280,14975,14976,14979,14980,14983,14984,14987],{},[5232,14977,14978],{},"Рядки 32–35:"," Метадані gRPC передаються через контекст у вигляді map ",[5211,14981,14982],{},"string → []string",". Функція ",[5211,14985,14986],{},"metadata.FromIncomingContext()"," витягує метадані із запиту.",[5280,14989,14990,14993,14994,14997,14998,15001],{},[5232,14991,14992],{},"Рядки 38–48:"," Шукаємо заголовок ",[5211,14995,14996],{},"authorization"," та перевіряємо, що він має формат ",[5211,14999,15000],{},"Bearer \u003Ctoken>",". Це стандартний формат для передачі токенів (RFC 6750).",[5280,15003,15004,15007,15008,15011,15012,15015],{},[5232,15005,15006],{},"Рядки 51–54:"," Викликаємо функцію валідації JWT (у реальній системі використовується бібліотека ",[5211,15009,15010],{},"github.com\u002Fgolang-jwt\u002Fjwt","), що перевіряє підпис, термін дії та витягує ",[5211,15013,15014],{},"user_id"," із токена.",[5280,15017,15018,15021,15022,15024],{},[5232,15019,15020],{},"Рядок 57:"," Додаємо ",[5211,15023,15014],{}," до контексту, що дозволяє обробникам RPC-методів отримати ідентифікатор автентифікованого користувача без повторної перевірки токена.",[5190,15026,15027,15918],{},[5193,15028,15030,15037,15042,15047,15061,15066,15084,15089,15374,15379,15683,15688,15909],{"icon":793,"title":15029},"🔐 Бібліотека golang-jwt\u002Fjwt",[5280,15031,15032,6758,15034],{},[5232,15033,7417],{},[5211,15035,15036],{},"github.com\u002Fgolang-jwt\u002Fjwt\u002Fv5",[5280,15038,15039,15041],{},[5232,15040,7395],{}," Повна реалізація JSON Web Tokens (JWT) для Go",[5280,15043,15044],{},[5232,15045,15046],{},"Основні можливості:",[5198,15048,15049,15052,15055,15058],{},[5201,15050,15051],{},"Генерація та валідація JWT токенів",[5201,15053,15054],{},"Підтримка різних алгоритмів підпису (HMAC, RSA, ECDSA, EdDSA)",[5201,15056,15057],{},"Перевірка claims (exp, nbf, iss, aud)",[5201,15059,15060],{},"Token rotation та Refresh tokens",[5280,15062,15063],{},[5232,15064,15065],{},"Встановлення:",[5456,15067,15069],{"className":7649,"code":15068,"language":7651,"meta":5461,"style":5461},"go get -u github.com\u002Fgolang-jwt\u002Fjwt\u002Fv5\n",[5211,15070,15071],{"__ignoreMap":5461},[5465,15072,15073,15075,15078,15081],{"class":5467,"line":5468},[5465,15074,5460],{"class":5538},[5465,15076,15077],{"class":5503}," get",[5465,15079,15080],{"class":5478}," -u",[5465,15082,15083],{"class":5503}," github.com\u002Fgolang-jwt\u002Fjwt\u002Fv5\n",[5280,15085,15086],{},[5232,15087,15088],{},"Приклад генерації токена:",[5456,15090,15092],{"className":5458,"code":15091,"language":5460,"meta":5461,"style":5461},"import (\n    \"time\"\n    \"github.com\u002Fgolang-jwt\u002Fjwt\u002Fv5\"\n)\n\ntype Claims struct {\n    UserID int64 `json:\"user_id\"`\n    jwt.RegisteredClaims\n}\n\nfunc generateJWT(userID int64, secret []byte) (string, error) {\n    claims := Claims{\n        UserID: userID,\n        RegisteredClaims: jwt.RegisteredClaims{\n            ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),\n            IssuedAt:  jwt.NewNumericDate(time.Now()),\n            Issuer:    \"userservice\",\n        },\n    }\n    \n    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)\n    return token.SignedString(secret)\n}\n",[5211,15093,15094,15100,15104,15109,15113,15117,15128,15139,15149,15153,15157,15191,15203,15214,15231,15275,15299,15311,15316,15320,15324,15353,15370],{"__ignoreMap":5461},[5465,15095,15096,15098],{"class":5467,"line":5468},[5465,15097,9812],{"class":5675},[5465,15099,7535],{"class":5489},[5465,15101,15102],{"class":5467,"line":5475},[5465,15103,11698],{"class":5503},[5465,15105,15106],{"class":5467,"line":5493},[5465,15107,15108],{"class":5503},"    \"github.com\u002Fgolang-jwt\u002Fjwt\u002Fv5\"\n",[5465,15110,15111],{"class":5467,"line":5507},[5465,15112,5591],{"class":5489},[5465,15114,15115],{"class":5467,"line":5519},[5465,15116,5529],{"emptyLinePlaceholder":5528},[5465,15118,15119,15121,15124,15126],{"class":5467,"line":5525},[5465,15120,5479],{"class":5478},[5465,15122,15123],{"class":5482}," Claims",[5465,15125,5486],{"class":5478},[5465,15127,5490],{"class":5489},[5465,15129,15130,15133,15136],{"class":5467,"line":5532},[5465,15131,15132],{"class":5496},"    UserID",[5465,15134,15135],{"class":5482}," int64",[5465,15137,15138],{"class":5503}," `json:\"user_id\"`\n",[5465,15140,15141,15144,15146],{"class":5467,"line":5565},[5465,15142,15143],{"class":5482},"    jwt",[5465,15145,5214],{"class":5489},[5465,15147,15148],{"class":5482},"RegisteredClaims\n",[5465,15150,15151],{"class":5467,"line":5594},[5465,15152,5522],{"class":5489},[5465,15154,15155],{"class":5467,"line":5600},[5465,15156,5529],{"emptyLinePlaceholder":5528},[5465,15158,15159,15161,15164,15166,15168,15170,15172,15175,15178,15181,15183,15185,15187,15189],{"class":5467,"line":5624},[5465,15160,5535],{"class":5478},[5465,15162,15163],{"class":5538}," generateJWT",[5465,15165,5542],{"class":5489},[5465,15167,14821],{"class":5496},[5465,15169,15135],{"class":5482},[5465,15171,5336],{"class":5489},[5465,15173,15174],{"class":5496},"secret",[5465,15176,15177],{"class":5489}," []",[5465,15179,15180],{"class":5482},"byte",[5465,15182,13838],{"class":5489},[5465,15184,6764],{"class":5482},[5465,15186,5336],{"class":5489},[5465,15188,5560],{"class":5482},[5465,15190,10148],{"class":5489},[5465,15192,15193,15196,15198,15201],{"class":5467,"line":5639},[5465,15194,15195],{"class":5496},"    claims",[5465,15197,5576],{"class":5489},[5465,15199,15200],{"class":5482},"Claims",[5465,15202,5926],{"class":5489},[5465,15204,15205,15208,15210,15212],{"class":5467,"line":5647},[5465,15206,15207],{"class":5496},"        UserID",[5465,15209,5807],{"class":5489},[5465,15211,14821],{"class":5496},[5465,15213,5636],{"class":5489},[5465,15215,15216,15219,15221,15224,15226,15229],{"class":5467,"line":5666},[5465,15217,15218],{"class":5496},"        RegisteredClaims",[5465,15220,5807],{"class":5489},[5465,15222,15223],{"class":5482},"jwt",[5465,15225,5214],{"class":5489},[5465,15227,15228],{"class":5482},"RegisteredClaims",[5465,15230,5926],{"class":5489},[5465,15232,15233,15236,15238,15240,15242,15245,15247,15249,15251,15253,15255,15258,15260,15263,15265,15267,15269,15272],{"class":5467,"line":5672},[5465,15234,15235],{"class":5496},"            ExpiresAt",[5465,15237,5807],{"class":5489},[5465,15239,15223],{"class":5496},[5465,15241,5214],{"class":5489},[5465,15243,15244],{"class":5538},"NewNumericDate",[5465,15246,5542],{"class":5489},[5465,15248,11994],{"class":5496},[5465,15250,5214],{"class":5489},[5465,15252,13862],{"class":5538},[5465,15254,10856],{"class":5489},[5465,15256,15257],{"class":5538},"Add",[5465,15259,5542],{"class":5489},[5465,15261,15262],{"class":7522},"24",[5465,15264,11388],{"class":5489},[5465,15266,11994],{"class":5496},[5465,15268,5214],{"class":5489},[5465,15270,15271],{"class":5496},"Hour",[5465,15273,15274],{"class":5489},")),\n",[5465,15276,15277,15280,15282,15284,15286,15288,15290,15292,15294,15296],{"class":5467,"line":5690},[5465,15278,15279],{"class":5496},"            IssuedAt",[5465,15281,5933],{"class":5489},[5465,15283,15223],{"class":5496},[5465,15285,5214],{"class":5489},[5465,15287,15244],{"class":5538},[5465,15289,5542],{"class":5489},[5465,15291,11994],{"class":5496},[5465,15293,5214],{"class":5489},[5465,15295,13862],{"class":5538},[5465,15297,15298],{"class":5489},"()),\n",[5465,15300,15301,15304,15306,15309],{"class":5467,"line":5699},[5465,15302,15303],{"class":5496},"            Issuer",[5465,15305,10460],{"class":5489},[5465,15307,15308],{"class":5503},"\"userservice\"",[5465,15310,5636],{"class":5489},[5465,15312,15313],{"class":5467,"line":5705},[5465,15314,15315],{"class":5489},"        },\n",[5465,15317,15318],{"class":5467,"line":5727},[5465,15319,5702],{"class":5489},[5465,15321,15322],{"class":5467,"line":5732},[5465,15323,5597],{"class":5489},[5465,15325,15326,15328,15330,15332,15334,15337,15339,15341,15343,15346,15348,15351],{"class":5467,"line":5755},[5465,15327,14633],{"class":5496},[5465,15329,5576],{"class":5489},[5465,15331,15223],{"class":5496},[5465,15333,5214],{"class":5489},[5465,15335,15336],{"class":5538},"NewWithClaims",[5465,15338,5542],{"class":5489},[5465,15340,15223],{"class":5496},[5465,15342,5214],{"class":5489},[5465,15344,15345],{"class":5496},"SigningMethodHS256",[5465,15347,5336],{"class":5489},[5465,15349,15350],{"class":5496},"claims",[5465,15352,5591],{"class":5489},[5465,15354,15355,15357,15359,15361,15364,15366,15368],{"class":5467,"line":5786},[5465,15356,5844],{"class":5675},[5465,15358,14666],{"class":5496},[5465,15360,5214],{"class":5489},[5465,15362,15363],{"class":5538},"SignedString",[5465,15365,5542],{"class":5489},[5465,15367,15174],{"class":5496},[5465,15369,5591],{"class":5489},[5465,15371,15372],{"class":5467,"line":5831},[5465,15373,5522],{"class":5489},[5280,15375,15376],{},[5232,15377,15378],{},"Приклад валідації токена:",[5456,15380,15382],{"className":5458,"code":15381,"language":5460,"meta":5461,"style":5461},"func validateJWT(tokenString string, secret []byte) (*Claims, error) {\n    token, err := jwt.ParseWithClaims(\n        tokenString,\n        &Claims{},\n        func(token *jwt.Token) (interface{}, error) {\n            \u002F\u002F Перевірка алгоритму підпису\n            if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {\n                return nil, fmt.Errorf(\"unexpected signing method\")\n            }\n            return secret, nil\n        },\n    )\n    \n    if err != nil {\n        return nil, err\n    }\n    \n    if claims, ok := token.Claims.(*Claims); ok && token.Valid {\n        return claims, nil\n    }\n    \n    return nil, fmt.Errorf(\"invalid token\")\n}\n",[5211,15383,15384,15415,15434,15441,15450,15478,15483,15521,15544,15549,15560,15564,15568,15572,15584,15594,15598,15602,15641,15651,15655,15659,15679],{"__ignoreMap":5461},[5465,15385,15386,15388,15390,15392,15395,15397,15399,15401,15403,15405,15407,15409,15411,15413],{"class":5467,"line":5468},[5465,15387,5535],{"class":5478},[5465,15389,14868],{"class":5538},[5465,15391,5542],{"class":5489},[5465,15393,15394],{"class":5496},"tokenString",[5465,15396,5513],{"class":5482},[5465,15398,5336],{"class":5489},[5465,15400,15174],{"class":5496},[5465,15402,15177],{"class":5489},[5465,15404,15180],{"class":5482},[5465,15406,10135],{"class":5489},[5465,15408,15200],{"class":5482},[5465,15410,5336],{"class":5489},[5465,15412,5560],{"class":5482},[5465,15414,10148],{"class":5489},[5465,15416,15417,15419,15421,15423,15425,15427,15429,15432],{"class":5467,"line":5475},[5465,15418,14633],{"class":5496},[5465,15420,5336],{"class":5489},[5465,15422,5608],{"class":5496},[5465,15424,5576],{"class":5489},[5465,15426,15223],{"class":5496},[5465,15428,5214],{"class":5489},[5465,15430,15431],{"class":5538},"ParseWithClaims",[5465,15433,5621],{"class":5489},[5465,15435,15436,15439],{"class":5467,"line":5493},[5465,15437,15438],{"class":5496},"        tokenString",[5465,15440,5636],{"class":5489},[5465,15442,15443,15446,15448],{"class":5467,"line":5507},[5465,15444,15445],{"class":5489},"        &",[5465,15447,15200],{"class":5482},[5465,15449,13802],{"class":5489},[5465,15451,15452,15455,15457,15459,15461,15463,15465,15468,15470,15472,15474,15476],{"class":5467,"line":5519},[5465,15453,15454],{"class":5478},"        func",[5465,15456,5542],{"class":5489},[5465,15458,14737],{"class":5496},[5465,15460,10122],{"class":5489},[5465,15462,15223],{"class":5482},[5465,15464,5214],{"class":5489},[5465,15466,15467],{"class":5482},"Token",[5465,15469,13838],{"class":5489},[5465,15471,13841],{"class":5478},[5465,15473,13844],{"class":5489},[5465,15475,5560],{"class":5482},[5465,15477,10148],{"class":5489},[5465,15479,15480],{"class":5467,"line":5525},[5465,15481,15482],{"class":5471},"            \u002F\u002F Перевірка алгоритму підпису\n",[5465,15484,15485,15488,15491,15493,15495,15497,15499,15501,15504,15507,15509,15511,15514,15517,15519],{"class":5467,"line":5532},[5465,15486,15487],{"class":5675},"            if",[5465,15489,15490],{"class":5496}," _",[5465,15492,5336],{"class":5489},[5465,15494,13297],{"class":5496},[5465,15496,5576],{"class":5489},[5465,15498,14737],{"class":5496},[5465,15500,5214],{"class":5489},[5465,15502,15503],{"class":5496},"Method",[5465,15505,15506],{"class":5489},".(*",[5465,15508,15223],{"class":5482},[5465,15510,5214],{"class":5489},[5465,15512,15513],{"class":5482},"SigningMethodHMAC",[5465,15515,15516],{"class":5489},"); !",[5465,15518,13297],{"class":5496},[5465,15520,5490],{"class":5489},[5465,15522,15523,15526,15528,15530,15533,15535,15537,15539,15542],{"class":5467,"line":5565},[5465,15524,15525],{"class":5675},"                return",[5465,15527,10179],{"class":5478},[5465,15529,5336],{"class":5489},[5465,15531,15532],{"class":5496},"fmt",[5465,15534,5214],{"class":5489},[5465,15536,5796],{"class":5538},[5465,15538,5542],{"class":5489},[5465,15540,15541],{"class":5503},"\"unexpected signing method\"",[5465,15543,5591],{"class":5489},[5465,15545,15546],{"class":5467,"line":5594},[5465,15547,15548],{"class":5489},"            }\n",[5465,15550,15551,15553,15556,15558],{"class":5467,"line":5600},[5465,15552,10900],{"class":5675},[5465,15554,15555],{"class":5496}," secret",[5465,15557,5336],{"class":5489},[5465,15559,10604],{"class":5478},[5465,15561,15562],{"class":5467,"line":5624},[5465,15563,15315],{"class":5489},[5465,15565,15566],{"class":5467,"line":5639},[5465,15567,5669],{"class":5489},[5465,15569,15570],{"class":5467,"line":5647},[5465,15571,5597],{"class":5489},[5465,15573,15574,15576,15578,15580,15582],{"class":5467,"line":5666},[5465,15575,5676],{"class":5675},[5465,15577,5679],{"class":5496},[5465,15579,5682],{"class":5489},[5465,15581,5685],{"class":5478},[5465,15583,5490],{"class":5489},[5465,15585,15586,15588,15590,15592],{"class":5467,"line":5672},[5465,15587,5693],{"class":5675},[5465,15589,10179],{"class":5478},[5465,15591,5336],{"class":5489},[5465,15593,14138],{"class":5496},[5465,15595,15596],{"class":5467,"line":5690},[5465,15597,5702],{"class":5489},[5465,15599,15600],{"class":5467,"line":5699},[5465,15601,5597],{"class":5489},[5465,15603,15604,15606,15609,15611,15613,15615,15617,15619,15621,15623,15625,15627,15629,15632,15634,15636,15639],{"class":5467,"line":5705},[5465,15605,5676],{"class":5675},[5465,15607,15608],{"class":5496}," claims",[5465,15610,5336],{"class":5489},[5465,15612,13297],{"class":5496},[5465,15614,5576],{"class":5489},[5465,15616,14737],{"class":5496},[5465,15618,5214],{"class":5489},[5465,15620,15200],{"class":5496},[5465,15622,15506],{"class":5489},[5465,15624,15200],{"class":5482},[5465,15626,10960],{"class":5489},[5465,15628,13297],{"class":5496},[5465,15630,15631],{"class":5489}," && ",[5465,15633,14737],{"class":5496},[5465,15635,5214],{"class":5489},[5465,15637,15638],{"class":5496},"Valid",[5465,15640,5490],{"class":5489},[5465,15642,15643,15645,15647,15649],{"class":5467,"line":5727},[5465,15644,5693],{"class":5675},[5465,15646,15608],{"class":5496},[5465,15648,5336],{"class":5489},[5465,15650,10604],{"class":5478},[5465,15652,15653],{"class":5467,"line":5732},[5465,15654,5702],{"class":5489},[5465,15656,15657],{"class":5467,"line":5755},[5465,15658,5597],{"class":5489},[5465,15660,15661,15663,15665,15667,15669,15671,15673,15675,15677],{"class":5467,"line":5786},[5465,15662,5844],{"class":5675},[5465,15664,10179],{"class":5478},[5465,15666,5336],{"class":5489},[5465,15668,15532],{"class":5496},[5465,15670,5214],{"class":5489},[5465,15672,5796],{"class":5538},[5465,15674,5542],{"class":5489},[5465,15676,14778],{"class":5503},[5465,15678,5591],{"class":5489},[5465,15680,15681],{"class":5467,"line":5831},[5465,15682,5522],{"class":5489},[5280,15684,15685],{},[5232,15686,15687],{},"Використання в інтерцепторі:",[5456,15689,15691],{"className":5458,"code":15690,"language":5460,"meta":5461,"style":5461},"func UnaryAuthInterceptor(secret []byte) grpc.UnaryServerInterceptor {\n    return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {\n        \u002F\u002F ... отримання токена з метаданих\n        \n        claims, err := validateJWT(token, secret)\n        if err != nil {\n            return nil, status.Error(codes.Unauthenticated, \"invalid token\")\n        }\n        \n        ctx = context.WithValue(ctx, \"user_id\", claims.UserID)\n        return handler(ctx, req)\n    }\n}\n",[5211,15692,15693,15718,15773,15778,15782,15805,15817,15845,15849,15853,15885,15901,15905],{"__ignoreMap":5461},[5465,15694,15695,15697,15699,15701,15703,15705,15707,15709,15711,15713,15716],{"class":5467,"line":5468},[5465,15696,5535],{"class":5478},[5465,15698,14305],{"class":5538},[5465,15700,5542],{"class":5489},[5465,15702,15174],{"class":5496},[5465,15704,15177],{"class":5489},[5465,15706,15180],{"class":5482},[5465,15708,5557],{"class":5489},[5465,15710,7555],{"class":5482},[5465,15712,5214],{"class":5489},[5465,15714,15715],{"class":5482},"UnaryServerInterceptor",[5465,15717,5490],{"class":5489},[5465,15719,15720,15722,15725,15727,15729,15731,15733,15735,15737,15739,15741,15743,15745,15747,15749,15751,15753,15755,15757,15759,15761,15763,15765,15767,15769,15771],{"class":5467,"line":5475},[5465,15721,5844],{"class":5675},[5465,15723,15724],{"class":5478}," func",[5465,15726,5542],{"class":5489},[5465,15728,5917],{"class":5496},[5465,15730,10107],{"class":5482},[5465,15732,5214],{"class":5489},[5465,15734,10112],{"class":5482},[5465,15736,5336],{"class":5489},[5465,15738,10429],{"class":5496},[5465,15740,13799],{"class":5478},[5465,15742,13844],{"class":5489},[5465,15744,13890],{"class":5496},[5465,15746,10122],{"class":5489},[5465,15748,7555],{"class":5482},[5465,15750,5214],{"class":5489},[5465,15752,13816],{"class":5482},[5465,15754,5336],{"class":5489},[5465,15756,13919],{"class":5496},[5465,15758,13826],{"class":5482},[5465,15760,5214],{"class":5489},[5465,15762,13831],{"class":5482},[5465,15764,13838],{"class":5489},[5465,15766,13841],{"class":5478},[5465,15768,13844],{"class":5489},[5465,15770,5560],{"class":5482},[5465,15772,10148],{"class":5489},[5465,15774,15775],{"class":5467,"line":5493},[5465,15776,15777],{"class":5471},"        \u002F\u002F ... отримання токена з метаданих\n",[5465,15779,15780],{"class":5467,"line":5507},[5465,15781,10933],{"class":5489},[5465,15783,15784,15787,15789,15791,15793,15795,15797,15799,15801,15803],{"class":5467,"line":5519},[5465,15785,15786],{"class":5496},"        claims",[5465,15788,5336],{"class":5489},[5465,15790,5608],{"class":5496},[5465,15792,5576],{"class":5489},[5465,15794,14732],{"class":5538},[5465,15796,5542],{"class":5489},[5465,15798,14737],{"class":5496},[5465,15800,5336],{"class":5489},[5465,15802,15174],{"class":5496},[5465,15804,5591],{"class":5489},[5465,15806,15807,15809,15811,15813,15815],{"class":5467,"line":5525},[5465,15808,10843],{"class":5675},[5465,15810,5679],{"class":5496},[5465,15812,5682],{"class":5489},[5465,15814,5685],{"class":5478},[5465,15816,5490],{"class":5489},[5465,15818,15819,15821,15823,15825,15827,15829,15831,15833,15835,15837,15839,15841,15843],{"class":5467,"line":5532},[5465,15820,10900],{"class":5675},[5465,15822,10179],{"class":5478},[5465,15824,5336],{"class":5489},[5465,15826,10184],{"class":5496},[5465,15828,5214],{"class":5489},[5465,15830,10189],{"class":5538},[5465,15832,5542],{"class":5489},[5465,15834,10194],{"class":5496},[5465,15836,5214],{"class":5489},[5465,15838,14525],{"class":5496},[5465,15840,5336],{"class":5489},[5465,15842,14778],{"class":5503},[5465,15844,5591],{"class":5489},[5465,15846,15847],{"class":5467,"line":5565},[5465,15848,10928],{"class":5489},[5465,15850,15851],{"class":5467,"line":5594},[5465,15852,10933],{"class":5489},[5465,15854,15855,15858,15860,15862,15864,15866,15868,15870,15872,15874,15876,15878,15880,15883],{"class":5467,"line":5600},[5465,15856,15857],{"class":5496},"        ctx",[5465,15859,14800],{"class":5489},[5465,15861,11969],{"class":5496},[5465,15863,5214],{"class":5489},[5465,15865,14807],{"class":5538},[5465,15867,5542],{"class":5489},[5465,15869,5917],{"class":5496},[5465,15871,5336],{"class":5489},[5465,15873,14816],{"class":5503},[5465,15875,5336],{"class":5489},[5465,15877,15350],{"class":5496},[5465,15879,5214],{"class":5489},[5465,15881,15882],{"class":5496},"UserID",[5465,15884,5591],{"class":5489},[5465,15886,15887,15889,15891,15893,15895,15897,15899],{"class":5467,"line":5624},[5465,15888,5693],{"class":5675},[5465,15890,14444],{"class":5538},[5465,15892,5542],{"class":5489},[5465,15894,5917],{"class":5496},[5465,15896,5336],{"class":5489},[5465,15898,10429],{"class":5496},[5465,15900,5591],{"class":5489},[5465,15902,15903],{"class":5467,"line":5639},[5465,15904,5702],{"class":5489},[5465,15906,15907],{"class":5467,"line":5647},[5465,15908,5522],{"class":5489},[5280,15910,15911,6758,15913],{},[5232,15912,7435],{},[7379,15914,15917],{"href":15915,"rel":15916},"https:\u002F\u002Fpkg.go.dev\u002Fgithub.com\u002Fgolang-jwt\u002Fjwt\u002Fv5",[7383],"pkg.go.dev\u002Fgithub.com\u002Fgolang-jwt\u002Fjwt\u002Fv5",[5193,15919,15921,15929,15934,15939,15944,16182,16187,16423,16428,16514],{"icon":4112,"title":15920},"🔒 Пакет crypto\u002Ftls",[5280,15922,15923,6758,15925,15928],{},[5232,15924,7417],{},[5211,15926,15927],{},"crypto\u002Ftls"," (стандартна бібліотека Go)",[5280,15930,15931,15933],{},[5232,15932,7395],{}," Реалізація протоколу TLS 1.0–1.3 для безпечної комунікації",[5280,15935,15936],{},[5232,15937,15938],{},"Використання у gRPC:",[5280,15940,15941],{},[5232,15942,15943],{},"Серверна конфігурація з TLS:",[5456,15945,15947],{"className":5458,"code":15946,"language":5460,"meta":5461,"style":5461},"import (\n    \"crypto\u002Ftls\"\n    \"google.golang.org\u002Fgrpc\"\n    \"google.golang.org\u002Fgrpc\u002Fcredentials\"\n)\n\nfunc main() {\n    \u002F\u002F Завантажуємо сертифікат та ключ\n    cert, err := tls.LoadX509KeyPair(\"server.crt\", \"server.key\")\n    if err != nil {\n        log.Fatal(err)\n    }\n    \n    tlsConfig := &tls.Config{\n        Certificates: []tls.Certificate{cert},\n        MinVersion:   tls.VersionTLS13,  \u002F\u002F Вимагаємо TLS 1.3\n    }\n    \n    creds := credentials.NewTLS(tlsConfig)\n    server := grpc.NewServer(grpc.Creds(creds))\n    \u002F\u002F ... реєстрація сервісів\n}\n",[5211,15948,15949,15955,15960,15964,15969,15973,15977,15985,15990,16021,16033,16048,16052,16056,16072,16095,16114,16118,16122,16144,16173,16178],{"__ignoreMap":5461},[5465,15950,15951,15953],{"class":5467,"line":5468},[5465,15952,9812],{"class":5675},[5465,15954,7535],{"class":5489},[5465,15956,15957],{"class":5467,"line":5475},[5465,15958,15959],{"class":5503},"    \"crypto\u002Ftls\"\n",[5465,15961,15962],{"class":5467,"line":5493},[5465,15963,11246],{"class":5503},[5465,15965,15966],{"class":5467,"line":5507},[5465,15967,15968],{"class":5503},"    \"google.golang.org\u002Fgrpc\u002Fcredentials\"\n",[5465,15970,15971],{"class":5467,"line":5519},[5465,15972,5591],{"class":5489},[5465,15974,15975],{"class":5467,"line":5525},[5465,15976,5529],{"emptyLinePlaceholder":5528},[5465,15978,15979,15981,15983],{"class":5467,"line":5532},[5465,15980,5535],{"class":5478},[5465,15982,11266],{"class":5538},[5465,15984,11269],{"class":5489},[5465,15986,15987],{"class":5467,"line":5565},[5465,15988,15989],{"class":5471},"    \u002F\u002F Завантажуємо сертифікат та ключ\n",[5465,15991,15992,15995,15997,15999,16001,16004,16006,16009,16011,16014,16016,16019],{"class":5467,"line":5594},[5465,15993,15994],{"class":5496},"    cert",[5465,15996,5336],{"class":5489},[5465,15998,5608],{"class":5496},[5465,16000,5576],{"class":5489},[5465,16002,16003],{"class":5496},"tls",[5465,16005,5214],{"class":5489},[5465,16007,16008],{"class":5538},"LoadX509KeyPair",[5465,16010,5542],{"class":5489},[5465,16012,16013],{"class":5503},"\"server.crt\"",[5465,16015,5336],{"class":5489},[5465,16017,16018],{"class":5503},"\"server.key\"",[5465,16020,5591],{"class":5489},[5465,16022,16023,16025,16027,16029,16031],{"class":5467,"line":5600},[5465,16024,5676],{"class":5675},[5465,16026,5679],{"class":5496},[5465,16028,5682],{"class":5489},[5465,16030,5685],{"class":5478},[5465,16032,5490],{"class":5489},[5465,16034,16035,16037,16039,16042,16044,16046],{"class":5467,"line":5624},[5465,16036,11046],{"class":5496},[5465,16038,5214],{"class":5489},[5465,16040,16041],{"class":5538},"Fatal",[5465,16043,5542],{"class":5489},[5465,16045,5608],{"class":5496},[5465,16047,5591],{"class":5489},[5465,16049,16050],{"class":5467,"line":5639},[5465,16051,5702],{"class":5489},[5465,16053,16054],{"class":5467,"line":5647},[5465,16055,5597],{"class":5489},[5465,16057,16058,16061,16063,16065,16067,16070],{"class":5467,"line":5666},[5465,16059,16060],{"class":5496},"    tlsConfig",[5465,16062,10392],{"class":5489},[5465,16064,16003],{"class":5482},[5465,16066,5214],{"class":5489},[5465,16068,16069],{"class":5482},"Config",[5465,16071,5926],{"class":5489},[5465,16073,16074,16077,16080,16082,16084,16087,16089,16092],{"class":5467,"line":5672},[5465,16075,16076],{"class":5496},"        Certificates",[5465,16078,16079],{"class":5489},": []",[5465,16081,16003],{"class":5482},[5465,16083,5214],{"class":5489},[5465,16085,16086],{"class":5482},"Certificate",[5465,16088,10592],{"class":5489},[5465,16090,16091],{"class":5496},"cert",[5465,16093,16094],{"class":5489},"},\n",[5465,16096,16097,16100,16102,16104,16106,16109,16111],{"class":5467,"line":5690},[5465,16098,16099],{"class":5496},"        MinVersion",[5465,16101,12362],{"class":5489},[5465,16103,16003],{"class":5496},[5465,16105,5214],{"class":5489},[5465,16107,16108],{"class":5496},"VersionTLS13",[5465,16110,14403],{"class":5489},[5465,16112,16113],{"class":5471},"\u002F\u002F Вимагаємо TLS 1.3\n",[5465,16115,16116],{"class":5467,"line":5699},[5465,16117,5702],{"class":5489},[5465,16119,16120],{"class":5467,"line":5705},[5465,16121,5597],{"class":5489},[5465,16123,16124,16127,16129,16132,16134,16137,16139,16142],{"class":5467,"line":5727},[5465,16125,16126],{"class":5496},"    creds",[5465,16128,5576],{"class":5489},[5465,16130,16131],{"class":5496},"credentials",[5465,16133,5214],{"class":5489},[5465,16135,16136],{"class":5538},"NewTLS",[5465,16138,5542],{"class":5489},[5465,16140,16141],{"class":5496},"tlsConfig",[5465,16143,5591],{"class":5489},[5465,16145,16146,16149,16151,16153,16155,16157,16159,16161,16163,16166,16168,16171],{"class":5467,"line":5732},[5465,16147,16148],{"class":5496},"    server",[5465,16150,5576],{"class":5489},[5465,16152,7555],{"class":5496},[5465,16154,5214],{"class":5489},[5465,16156,11368],{"class":5538},[5465,16158,5542],{"class":5489},[5465,16160,7555],{"class":5496},[5465,16162,5214],{"class":5489},[5465,16164,16165],{"class":5538},"Creds",[5465,16167,5542],{"class":5489},[5465,16169,16170],{"class":5496},"creds",[5465,16172,10799],{"class":5489},[5465,16174,16175],{"class":5467,"line":5755},[5465,16176,16177],{"class":5471},"    \u002F\u002F ... реєстрація сервісів\n",[5465,16179,16180],{"class":5467,"line":5786},[5465,16181,5522],{"class":5489},[5280,16183,16184],{},[5232,16185,16186],{},"Клієнтська конфігурація з TLS:",[5456,16188,16190],{"className":5458,"code":16189,"language":5460,"meta":5461,"style":5461},"func main() {\n    \u002F\u002F Завантажуємо CA сертифікат для перевірки сервера\n    caCert, err := os.ReadFile(\"ca.crt\")\n    if err != nil {\n        log.Fatal(err)\n    }\n    \n    certPool := x509.NewCertPool()\n    certPool.AppendCertsFromPEM(caCert)\n    \n    tlsConfig := &tls.Config{\n        RootCAs:    certPool,\n        MinVersion: tls.VersionTLS13,\n    }\n    \n    creds := credentials.NewTLS(tlsConfig)\n    \n    conn, err := grpc.Dial(\n        \"localhost:50051\",\n        grpc.WithTransportCredentials(creds),\n    )\n    \u002F\u002F ... використання клієнта\n}\n",[5211,16191,16192,16200,16205,16231,16243,16257,16261,16265,16282,16298,16302,16316,16328,16342,16346,16350,16368,16372,16390,16396,16410,16414,16419],{"__ignoreMap":5461},[5465,16193,16194,16196,16198],{"class":5467,"line":5468},[5465,16195,5535],{"class":5478},[5465,16197,11266],{"class":5538},[5465,16199,11269],{"class":5489},[5465,16201,16202],{"class":5467,"line":5475},[5465,16203,16204],{"class":5471},"    \u002F\u002F Завантажуємо CA сертифікат для перевірки сервера\n",[5465,16206,16207,16210,16212,16214,16216,16219,16221,16224,16226,16229],{"class":5467,"line":5493},[5465,16208,16209],{"class":5496},"    caCert",[5465,16211,5336],{"class":5489},[5465,16213,5608],{"class":5496},[5465,16215,5576],{"class":5489},[5465,16217,16218],{"class":5496},"os",[5465,16220,5214],{"class":5489},[5465,16222,16223],{"class":5538},"ReadFile",[5465,16225,5542],{"class":5489},[5465,16227,16228],{"class":5503},"\"ca.crt\"",[5465,16230,5591],{"class":5489},[5465,16232,16233,16235,16237,16239,16241],{"class":5467,"line":5507},[5465,16234,5676],{"class":5675},[5465,16236,5679],{"class":5496},[5465,16238,5682],{"class":5489},[5465,16240,5685],{"class":5478},[5465,16242,5490],{"class":5489},[5465,16244,16245,16247,16249,16251,16253,16255],{"class":5467,"line":5519},[5465,16246,11046],{"class":5496},[5465,16248,5214],{"class":5489},[5465,16250,16041],{"class":5538},[5465,16252,5542],{"class":5489},[5465,16254,5608],{"class":5496},[5465,16256,5591],{"class":5489},[5465,16258,16259],{"class":5467,"line":5525},[5465,16260,5702],{"class":5489},[5465,16262,16263],{"class":5467,"line":5532},[5465,16264,5597],{"class":5489},[5465,16266,16267,16270,16272,16275,16277,16280],{"class":5467,"line":5565},[5465,16268,16269],{"class":5496},"    certPool",[5465,16271,5576],{"class":5489},[5465,16273,16274],{"class":5496},"x509",[5465,16276,5214],{"class":5489},[5465,16278,16279],{"class":5538},"NewCertPool",[5465,16281,5724],{"class":5489},[5465,16283,16284,16286,16288,16291,16293,16296],{"class":5467,"line":5594},[5465,16285,16269],{"class":5496},[5465,16287,5214],{"class":5489},[5465,16289,16290],{"class":5538},"AppendCertsFromPEM",[5465,16292,5542],{"class":5489},[5465,16294,16295],{"class":5496},"caCert",[5465,16297,5591],{"class":5489},[5465,16299,16300],{"class":5467,"line":5600},[5465,16301,5597],{"class":5489},[5465,16303,16304,16306,16308,16310,16312,16314],{"class":5467,"line":5624},[5465,16305,16060],{"class":5496},[5465,16307,10392],{"class":5489},[5465,16309,16003],{"class":5482},[5465,16311,5214],{"class":5489},[5465,16313,16069],{"class":5482},[5465,16315,5926],{"class":5489},[5465,16317,16318,16321,16323,16326],{"class":5467,"line":5639},[5465,16319,16320],{"class":5496},"        RootCAs",[5465,16322,10460],{"class":5489},[5465,16324,16325],{"class":5496},"certPool",[5465,16327,5636],{"class":5489},[5465,16329,16330,16332,16334,16336,16338,16340],{"class":5467,"line":5647},[5465,16331,16099],{"class":5496},[5465,16333,5807],{"class":5489},[5465,16335,16003],{"class":5496},[5465,16337,5214],{"class":5489},[5465,16339,16108],{"class":5496},[5465,16341,5636],{"class":5489},[5465,16343,16344],{"class":5467,"line":5666},[5465,16345,5702],{"class":5489},[5465,16347,16348],{"class":5467,"line":5672},[5465,16349,5597],{"class":5489},[5465,16351,16352,16354,16356,16358,16360,16362,16364,16366],{"class":5467,"line":5690},[5465,16353,16126],{"class":5496},[5465,16355,5576],{"class":5489},[5465,16357,16131],{"class":5496},[5465,16359,5214],{"class":5489},[5465,16361,16136],{"class":5538},[5465,16363,5542],{"class":5489},[5465,16365,16141],{"class":5496},[5465,16367,5591],{"class":5489},[5465,16369,16370],{"class":5467,"line":5699},[5465,16371,5597],{"class":5489},[5465,16373,16374,16376,16378,16380,16382,16384,16386,16388],{"class":5467,"line":5705},[5465,16375,11743],{"class":5496},[5465,16377,5336],{"class":5489},[5465,16379,5608],{"class":5496},[5465,16381,5576],{"class":5489},[5465,16383,7555],{"class":5496},[5465,16385,5214],{"class":5489},[5465,16387,11756],{"class":5538},[5465,16389,5621],{"class":5489},[5465,16391,16392,16394],{"class":5467,"line":5727},[5465,16393,11763],{"class":5503},[5465,16395,5636],{"class":5489},[5465,16397,16398,16400,16402,16404,16406,16408],{"class":5467,"line":5732},[5465,16399,11375],{"class":5496},[5465,16401,5214],{"class":5489},[5465,16403,11774],{"class":5538},[5465,16405,5542],{"class":5489},[5465,16407,16170],{"class":5496},[5465,16409,5663],{"class":5489},[5465,16411,16412],{"class":5467,"line":5755},[5465,16413,5669],{"class":5489},[5465,16415,16416],{"class":5467,"line":5786},[5465,16417,16418],{"class":5471},"    \u002F\u002F ... використання клієнта\n",[5465,16420,16421],{"class":5467,"line":5831},[5465,16422,5522],{"class":5489},[5280,16424,16425],{},[5232,16426,16427],{},"Mutual TLS (mTLS):",[5456,16429,16431],{"className":5458,"code":16430,"language":5460,"meta":5461,"style":5461},"tlsConfig := &tls.Config{\n    Certificates: []tls.Certificate{serverCert},\n    ClientAuth:   tls.RequireAndVerifyClientCert,\n    ClientCAs:    clientCAPool,\n    MinVersion:   tls.VersionTLS13,\n}\n",[5211,16432,16433,16447,16467,16483,16495,16510],{"__ignoreMap":5461},[5465,16434,16435,16437,16439,16441,16443,16445],{"class":5467,"line":5468},[5465,16436,16141],{"class":5496},[5465,16438,10392],{"class":5489},[5465,16440,16003],{"class":5482},[5465,16442,5214],{"class":5489},[5465,16444,16069],{"class":5482},[5465,16446,5926],{"class":5489},[5465,16448,16449,16452,16454,16456,16458,16460,16462,16465],{"class":5467,"line":5475},[5465,16450,16451],{"class":5496},"    Certificates",[5465,16453,16079],{"class":5489},[5465,16455,16003],{"class":5482},[5465,16457,5214],{"class":5489},[5465,16459,16086],{"class":5482},[5465,16461,10592],{"class":5489},[5465,16463,16464],{"class":5496},"serverCert",[5465,16466,16094],{"class":5489},[5465,16468,16469,16472,16474,16476,16478,16481],{"class":5467,"line":5493},[5465,16470,16471],{"class":5496},"    ClientAuth",[5465,16473,12362],{"class":5489},[5465,16475,16003],{"class":5496},[5465,16477,5214],{"class":5489},[5465,16479,16480],{"class":5496},"RequireAndVerifyClientCert",[5465,16482,5636],{"class":5489},[5465,16484,16485,16488,16490,16493],{"class":5467,"line":5507},[5465,16486,16487],{"class":5496},"    ClientCAs",[5465,16489,10460],{"class":5489},[5465,16491,16492],{"class":5496},"clientCAPool",[5465,16494,5636],{"class":5489},[5465,16496,16497,16500,16502,16504,16506,16508],{"class":5467,"line":5519},[5465,16498,16499],{"class":5496},"    MinVersion",[5465,16501,12362],{"class":5489},[5465,16503,16003],{"class":5496},[5465,16505,5214],{"class":5489},[5465,16507,16108],{"class":5496},[5465,16509,5636],{"class":5489},[5465,16511,16512],{"class":5467,"line":5525},[5465,16513,5522],{"class":5489},[5280,16515,16516,6758,16518],{},[5232,16517,7435],{},[7379,16519,16522],{"href":16520,"rel":16521},"https:\u002F\u002Fpkg.go.dev\u002Fcrypto\u002Ftls",[7383],"pkg.go.dev\u002Fcrypto\u002Ftls",[5275,16524,16526],{"id":16525},"реєстрація-інтерцепторів-на-сервері","Реєстрація інтерцепторів на сервері",[5280,16528,16529,16530,8158],{},"Інтерцептори реєструються під час створення gRPC-сервера через опції ",[5211,16531,16532],{},"grpc.ServerOption",[5456,16534,16536],{"className":5458,"code":16535,"language":5460,"meta":5461,"style":5461},"\u002F\u002F server\u002Fmain.go\npackage main\n\nimport (\n    \"log\"\n    \"net\"\n    \n    pb \"github.com\u002Fexample\u002Fuserservice\u002Fpb\"\n    \"github.com\u002Fexample\u002Fuserservice\u002Fserver\u002Finterceptors\"\n    \"google.golang.org\u002Fgrpc\"\n)\n\nfunc main() {\n    listener, err := net.Listen(\"tcp\", \":50051\")\n    if err != nil {\n        log.Fatalf(\"Failed to listen: %v\", err)\n    }\n    \n    \u002F\u002F Створюємо gRPC-сервер із ланцюжком інтерцепторів\n    grpcServer := grpc.NewServer(\n        grpc.ChainUnaryInterceptor(\n            interceptors.UnaryLoggingInterceptor,   \u002F\u002F Першим логування\n            interceptors.UnaryAuthInterceptor,      \u002F\u002F Потім автентифікація\n        ),\n        \u002F\u002F Для Stream інтерцепторів використовується ChainStreamInterceptor\n    )\n    \n    \u002F\u002F Реєстрація сервісу\n    userService := NewUserServer()\n    pb.RegisterUserServiceServer(grpcServer, userService)\n    \n    log.Println(\"gRPC server with interceptors listening on :50051\")\n    \n    if err := grpcServer.Serve(listener); err != nil {\n        log.Fatalf(\"Failed to serve: %v\", err)\n    }\n}\n",[5211,16537,16538,16542,16548,16552,16558,16562,16566,16570,16576,16581,16585,16589,16593,16601,16627,16639,16661,16665,16669,16674,16688,16699,16715,16730,16735,16740,16744,16748,16753,16763,16781,16785,16800,16804,16832,16854,16858],{"__ignoreMap":5461},[5465,16539,16540],{"class":5467,"line":5468},[5465,16541,11206],{"class":5471},[5465,16543,16544,16546],{"class":5467,"line":5475},[5465,16545,9800],{"class":5478},[5465,16547,9803],{"class":5482},[5465,16549,16550],{"class":5467,"line":5493},[5465,16551,5529],{"emptyLinePlaceholder":5528},[5465,16553,16554,16556],{"class":5467,"line":5507},[5465,16555,9812],{"class":5675},[5465,16557,7535],{"class":5489},[5465,16559,16560],{"class":5467,"line":5519},[5465,16561,9829],{"class":5503},[5465,16563,16564],{"class":5467,"line":5525},[5465,16565,11231],{"class":5503},[5465,16567,16568],{"class":5467,"line":5532},[5465,16569,5597],{"class":5489},[5465,16571,16572,16574],{"class":5467,"line":5565},[5465,16573,9843],{"class":5496},[5465,16575,9846],{"class":5503},[5465,16577,16578],{"class":5467,"line":5594},[5465,16579,16580],{"class":5503},"    \"github.com\u002Fexample\u002Fuserservice\u002Fserver\u002Finterceptors\"\n",[5465,16582,16583],{"class":5467,"line":5600},[5465,16584,11246],{"class":5503},[5465,16586,16587],{"class":5467,"line":5624},[5465,16588,5591],{"class":5489},[5465,16590,16591],{"class":5467,"line":5639},[5465,16592,5529],{"emptyLinePlaceholder":5528},[5465,16594,16595,16597,16599],{"class":5467,"line":5647},[5465,16596,5535],{"class":5478},[5465,16598,11266],{"class":5538},[5465,16600,11269],{"class":5489},[5465,16602,16603,16605,16607,16609,16611,16613,16615,16617,16619,16621,16623,16625],{"class":5467,"line":5666},[5465,16604,11279],{"class":5496},[5465,16606,5336],{"class":5489},[5465,16608,5608],{"class":5496},[5465,16610,5576],{"class":5489},[5465,16612,11288],{"class":5496},[5465,16614,5214],{"class":5489},[5465,16616,11293],{"class":5538},[5465,16618,5542],{"class":5489},[5465,16620,11298],{"class":5503},[5465,16622,5336],{"class":5489},[5465,16624,11303],{"class":5503},[5465,16626,5591],{"class":5489},[5465,16628,16629,16631,16633,16635,16637],{"class":5467,"line":5672},[5465,16630,5676],{"class":5675},[5465,16632,5679],{"class":5496},[5465,16634,5682],{"class":5489},[5465,16636,5685],{"class":5478},[5465,16638,5490],{"class":5489},[5465,16640,16641,16643,16645,16647,16649,16651,16653,16655,16657,16659],{"class":5467,"line":5690},[5465,16642,11046],{"class":5496},[5465,16644,5214],{"class":5489},[5465,16646,11326],{"class":5538},[5465,16648,5542],{"class":5489},[5465,16650,11331],{"class":5503},[5465,16652,10887],{"class":5496},[5465,16654,5813],{"class":5503},[5465,16656,5336],{"class":5489},[5465,16658,5608],{"class":5496},[5465,16660,5591],{"class":5489},[5465,16662,16663],{"class":5467,"line":5699},[5465,16664,5702],{"class":5489},[5465,16666,16667],{"class":5467,"line":5705},[5465,16668,5597],{"class":5489},[5465,16670,16671],{"class":5467,"line":5727},[5465,16672,16673],{"class":5471},"    \u002F\u002F Створюємо gRPC-сервер із ланцюжком інтерцепторів\n",[5465,16675,16676,16678,16680,16682,16684,16686],{"class":5467,"line":5732},[5465,16677,11359],{"class":5496},[5465,16679,5576],{"class":5489},[5465,16681,7555],{"class":5496},[5465,16683,5214],{"class":5489},[5465,16685,11368],{"class":5538},[5465,16687,5621],{"class":5489},[5465,16689,16690,16692,16694,16697],{"class":5467,"line":5755},[5465,16691,11375],{"class":5496},[5465,16693,5214],{"class":5489},[5465,16695,16696],{"class":5538},"ChainUnaryInterceptor",[5465,16698,5621],{"class":5489},[5465,16700,16701,16704,16706,16709,16712],{"class":5467,"line":5786},[5465,16702,16703],{"class":5496},"            interceptors",[5465,16705,5214],{"class":5489},[5465,16707,16708],{"class":5496},"UnaryLoggingInterceptor",[5465,16710,16711],{"class":5489},",   ",[5465,16713,16714],{"class":5471},"\u002F\u002F Першим логування\n",[5465,16716,16717,16719,16721,16724,16727],{"class":5467,"line":5831},[5465,16718,16703],{"class":5496},[5465,16720,5214],{"class":5489},[5465,16722,16723],{"class":5496},"UnaryAuthInterceptor",[5465,16725,16726],{"class":5489},",      ",[5465,16728,16729],{"class":5471},"\u002F\u002F Потім автентифікація\n",[5465,16731,16732],{"class":5467,"line":5836},[5465,16733,16734],{"class":5489},"        ),\n",[5465,16736,16737],{"class":5467,"line":5841},[5465,16738,16739],{"class":5471},"        \u002F\u002F Для Stream інтерцепторів використовується ChainStreamInterceptor\n",[5465,16741,16742],{"class":5467,"line":5850},[5465,16743,5669],{"class":5489},[5465,16745,16746],{"class":5467,"line":6213},[5465,16747,5597],{"class":5489},[5465,16749,16750],{"class":5467,"line":6218},[5465,16751,16752],{"class":5471},"    \u002F\u002F Реєстрація сервісу\n",[5465,16754,16755,16757,16759,16761],{"class":5467,"line":6224},[5465,16756,11445],{"class":5496},[5465,16758,5576],{"class":5489},[5465,16760,11450],{"class":5538},[5465,16762,5724],{"class":5489},[5465,16764,16765,16767,16769,16771,16773,16775,16777,16779],{"class":5467,"line":6230},[5465,16766,9843],{"class":5496},[5465,16768,5214],{"class":5489},[5465,16770,11461],{"class":5538},[5465,16772,5542],{"class":5489},[5465,16774,11466],{"class":5496},[5465,16776,5336],{"class":5489},[5465,16778,11471],{"class":5496},[5465,16780,5591],{"class":5489},[5465,16782,16783],{"class":5467,"line":6236},[5465,16784,5597],{"class":5489},[5465,16786,16787,16789,16791,16793,16795,16798],{"class":5467,"line":6242},[5465,16788,10534],{"class":5496},[5465,16790,5214],{"class":5489},[5465,16792,11098],{"class":5538},[5465,16794,5542],{"class":5489},[5465,16796,16797],{"class":5503},"\"gRPC server with interceptors listening on :50051\"",[5465,16799,5591],{"class":5489},[5465,16801,16802],{"class":5467,"line":6248},[5465,16803,5597],{"class":5489},[5465,16805,16806,16808,16810,16812,16814,16816,16818,16820,16822,16824,16826,16828,16830],{"class":5467,"line":6254},[5465,16807,5676],{"class":5675},[5465,16809,5679],{"class":5496},[5465,16811,5576],{"class":5489},[5465,16813,11466],{"class":5496},[5465,16815,5214],{"class":5489},[5465,16817,11541],{"class":5538},[5465,16819,5542],{"class":5489},[5465,16821,11546],{"class":5496},[5465,16823,10960],{"class":5489},[5465,16825,5608],{"class":5496},[5465,16827,5682],{"class":5489},[5465,16829,5685],{"class":5478},[5465,16831,5490],{"class":5489},[5465,16833,16834,16836,16838,16840,16842,16844,16846,16848,16850,16852],{"class":5467,"line":6260},[5465,16835,11046],{"class":5496},[5465,16837,5214],{"class":5489},[5465,16839,11326],{"class":5538},[5465,16841,5542],{"class":5489},[5465,16843,11569],{"class":5503},[5465,16845,10887],{"class":5496},[5465,16847,5813],{"class":5503},[5465,16849,5336],{"class":5489},[5465,16851,5608],{"class":5496},[5465,16853,5591],{"class":5489},[5465,16855,16856],{"class":5467,"line":6265},[5465,16857,5702],{"class":5489},[5465,16859,16860],{"class":5467,"line":6270},[5465,16861,5522],{"class":5489},[5280,16863,16864,11603,16866,16869,16870,16873,16874,16877],{},[5232,16865,11596],{},[5211,16867,16868],{},"grpc.ChainUnaryInterceptor()"," дозволяє зареєструвати кілька інтерцепторів у вигляді ланцюжка. Порядок виклику: ",[5232,16871,16872],{},"зверху вниз"," на вході та ",[5232,16875,16876],{},"знизу вгору"," на виході. У нашому випадку:",[6403,16879,16880,16886,16892,16898,16904],{},[5201,16881,16882,16883,16885],{},"Запит потрапляє у ",[5211,16884,16708],{}," → логуємо початок.",[5201,16887,16888,16889,16891],{},"Передаємо у ",[5211,16890,16723],{}," → перевіряємо токен.",[5201,16893,16894,16895,16897],{},"Якщо автентифікація успішна, викликаємо ",[5211,16896,13919],{}," (бізнес-логіка).",[5201,16899,16900,16901,16903],{},"Відповідь повертається через ",[5211,16902,16723],{}," (нічого не робить).",[5201,16905,16900,16906,16908],{},[5211,16907,16708],{}," → логуємо результат.",[6839,16910,16911,16912,5321,16917,16920],{},"Для складних систем із багатьма інтерцепторами використовуйте бібліотеку ",[5232,16913,16914],{},[5211,16915,16916],{},"go-grpc-middleware",[5211,16918,16919],{},"github.com\u002Fgrpc-ecosystem\u002Fgo-grpc-middleware","), що надає готові інтерцептори для логування, метрик, трейсингу, валідації, rate limiting та повторних спроб. Це зменшує обсяг boilerplate-коду та забезпечує промислові best practices.",[5190,16922,16923,17151,17305],{},[5193,16924,16926,16933,16938,16943,16948,16968,16973,16984,16989,17009,17014,17034,17038,17143],{"icon":943,"title":16925},"📚 Бібліотека go-grpc-middleware",[5280,16927,16928,6758,16930],{},[5232,16929,7417],{},[5211,16931,16932],{},"github.com\u002Fgrpc-ecosystem\u002Fgo-grpc-middleware\u002Fv2",[5280,16934,16935,16937],{},[5232,16936,7395],{}," Колекція промислових інтерцепторів для gRPC",[5280,16939,16940],{},[5232,16941,16942],{},"Основні компоненти:",[5280,16944,16945],{},[5232,16946,16947],{},"Логування:",[5198,16949,16950,16965],{},[5201,16951,16952,16955,16956,5336,16959,5336,16962],{},[5211,16953,16954],{},"logging"," — структуроване логування з підтримкою ",[5211,16957,16958],{},"slog",[5211,16960,16961],{},"zap",[5211,16963,16964],{},"logrus",[5201,16966,16967],{},"Автоматичне логування запитів, відповідей, помилок та тривалості",[5280,16969,16970],{},[5232,16971,16972],{},"Моніторинг та метрики:",[5198,16974,16975,16981],{},[5201,16976,16977,16980],{},[5211,16978,16979],{},"metrics"," — експорт метрик для Prometheus",[5201,16982,16983],{},"Автоматичний підрахунок RPC викликів, помилок, латентності",[5280,16985,16986],{},[5232,16987,16988],{},"Безпека:",[5198,16990,16991,16997,17003],{},[5201,16992,16993,16996],{},[5211,16994,16995],{},"auth"," — валідація токенів (JWT, OAuth2)",[5201,16998,16999,17002],{},[5211,17000,17001],{},"ratelimit"," — обмеження частоти запитів",[5201,17004,17005,17008],{},[5211,17006,17007],{},"validator"," — валідація Protobuf повідомлень через теги",[5280,17010,17011],{},[5232,17012,17013],{},"Надійність:",[5198,17015,17016,17022,17028],{},[5201,17017,17018,17021],{},[5211,17019,17020],{},"retry"," — автоматичні повторні спроби із exponential backoff",[5201,17023,17024,17027],{},[5211,17025,17026],{},"timeout"," — автоматичні таймаути для викликів",[5201,17029,17030,17033],{},[5211,17031,17032],{},"recovery"," — відновлення після panic у обробниках",[5280,17035,17036],{},[5232,17037,9576],{},[5456,17039,17041],{"className":5458,"code":17040,"language":5460,"meta":5461,"style":5461},"import (\n    grpc_middleware \"github.com\u002Fgrpc-ecosystem\u002Fgo-grpc-middleware\u002Fv2\"\n    grpc_recovery \"github.com\u002Fgrpc-ecosystem\u002Fgo-grpc-middleware\u002Fv2\u002Finterceptors\u002Frecovery\"\n    grpc_logging \"github.com\u002Fgrpc-ecosystem\u002Fgo-grpc-middleware\u002Fv2\u002Finterceptors\u002Flogging\"\n)\n\nserver := grpc.NewServer(\n    grpc.ChainUnaryInterceptor(\n        grpc_recovery.UnaryServerInterceptor(),\n        grpc_logging.UnaryServerInterceptor(logger),\n    ),\n)\n",[5211,17042,17043,17049,17057,17065,17073,17077,17081,17096,17107,17118,17134,17139],{"__ignoreMap":5461},[5465,17044,17045,17047],{"class":5467,"line":5468},[5465,17046,9812],{"class":5675},[5465,17048,7535],{"class":5489},[5465,17050,17051,17054],{"class":5467,"line":5475},[5465,17052,17053],{"class":5496},"    grpc_middleware",[5465,17055,17056],{"class":5503}," \"github.com\u002Fgrpc-ecosystem\u002Fgo-grpc-middleware\u002Fv2\"\n",[5465,17058,17059,17062],{"class":5467,"line":5493},[5465,17060,17061],{"class":5496},"    grpc_recovery",[5465,17063,17064],{"class":5503}," \"github.com\u002Fgrpc-ecosystem\u002Fgo-grpc-middleware\u002Fv2\u002Finterceptors\u002Frecovery\"\n",[5465,17066,17067,17070],{"class":5467,"line":5507},[5465,17068,17069],{"class":5496},"    grpc_logging",[5465,17071,17072],{"class":5503}," \"github.com\u002Fgrpc-ecosystem\u002Fgo-grpc-middleware\u002Fv2\u002Finterceptors\u002Flogging\"\n",[5465,17074,17075],{"class":5467,"line":5519},[5465,17076,5591],{"class":5489},[5465,17078,17079],{"class":5467,"line":5525},[5465,17080,5529],{"emptyLinePlaceholder":5528},[5465,17082,17083,17086,17088,17090,17092,17094],{"class":5467,"line":5532},[5465,17084,17085],{"class":5496},"server",[5465,17087,5576],{"class":5489},[5465,17089,7555],{"class":5496},[5465,17091,5214],{"class":5489},[5465,17093,11368],{"class":5538},[5465,17095,5621],{"class":5489},[5465,17097,17098,17101,17103,17105],{"class":5467,"line":5565},[5465,17099,17100],{"class":5496},"    grpc",[5465,17102,5214],{"class":5489},[5465,17104,16696],{"class":5538},[5465,17106,5621],{"class":5489},[5465,17108,17109,17112,17114,17116],{"class":5467,"line":5594},[5465,17110,17111],{"class":5496},"        grpc_recovery",[5465,17113,5214],{"class":5489},[5465,17115,15715],{"class":5538},[5465,17117,10436],{"class":5489},[5465,17119,17120,17123,17125,17127,17129,17132],{"class":5467,"line":5600},[5465,17121,17122],{"class":5496},"        grpc_logging",[5465,17124,5214],{"class":5489},[5465,17126,15715],{"class":5538},[5465,17128,5542],{"class":5489},[5465,17130,17131],{"class":5496},"logger",[5465,17133,5663],{"class":5489},[5465,17135,17136],{"class":5467,"line":5624},[5465,17137,17138],{"class":5489},"    ),\n",[5465,17140,17141],{"class":5467,"line":5639},[5465,17142,5591],{"class":5489},[5280,17144,17145,6758,17147],{},[5232,17146,7435],{},[7379,17148,16919],{"href":17149,"rel":17150},"https:\u002F\u002Fgithub.com\u002Fgrpc-ecosystem\u002Fgo-grpc-middleware",[7383],[5193,17152,17155,17162,17171,17175,17212,17216,17230,17235,17297],{"icon":17153,"title":17154},"i-lucide-terminal","🔍 Інструмент grpcurl",[5280,17156,17157,6758,17159],{},[5232,17158,7377],{},[5211,17160,17161],{},"github.com\u002Ffullstorydev\u002Fgrpcurl",[5280,17163,17164,17166,17167,17170],{},[5232,17165,7395],{}," CLI-інструмент для тестування gRPC API (аналог ",[5211,17168,17169],{},"curl"," для gRPC)",[5280,17172,17173],{},[5232,17174,15065],{},[5456,17176,17178],{"className":7649,"code":17177,"language":7651,"meta":5461,"style":5461},"# macOS\nbrew install grpcurl\n\n# Go Install\ngo install github.com\u002Ffullstorydev\u002Fgrpcurl\u002Fcmd\u002Fgrpcurl@latest\n",[5211,17179,17180,17185,17194,17198,17203],{"__ignoreMap":5461},[5465,17181,17182],{"class":5467,"line":5468},[5465,17183,17184],{"class":5471},"# macOS\n",[5465,17186,17187,17189,17191],{"class":5467,"line":5475},[5465,17188,7663],{"class":5538},[5465,17190,7666],{"class":5503},[5465,17192,17193],{"class":5503}," grpcurl\n",[5465,17195,17196],{"class":5467,"line":5493},[5465,17197,5529],{"emptyLinePlaceholder":5528},[5465,17199,17200],{"class":5467,"line":5507},[5465,17201,17202],{"class":5471},"# Go Install\n",[5465,17204,17205,17207,17209],{"class":5467,"line":5519},[5465,17206,5460],{"class":5538},[5465,17208,7666],{"class":5503},[5465,17210,17211],{"class":5503}," github.com\u002Ffullstorydev\u002Fgrpcurl\u002Fcmd\u002Fgrpcurl@latest\n",[5280,17213,17214],{},[5232,17215,15046],{},[5198,17217,17218,17221,17224,17227],{},[5201,17219,17220],{},"Виклик RPC-методів через командний рядок",[5201,17222,17223],{},"Автоматичне виявлення сервісів через gRPC Reflection",[5201,17225,17226],{},"Передача JSON-даних у Protobuf формат",[5201,17228,17229],{},"Додавання метаданих (JWT токени, custom headers)",[5280,17231,17232],{},[5232,17233,17234],{},"Приклади команд:",[5456,17236,17238],{"className":7649,"code":17237,"language":7651,"meta":5461,"style":5461},"# Список сервісів\ngrpcurl -plaintext localhost:50051 list\n\n# Виклик методу\ngrpcurl -plaintext \\\n  -d '{\"name\": \"Alice\"}' \\\n  localhost:50051 \\\n  userservice.UserService\u002FCreateUser\n",[5211,17239,17240,17245,17258,17262,17267,17275,17285,17292],{"__ignoreMap":5461},[5465,17241,17242],{"class":5467,"line":5468},[5465,17243,17244],{"class":5471},"# Список сервісів\n",[5465,17246,17247,17249,17252,17255],{"class":5467,"line":5475},[5465,17248,11624],{"class":5538},[5465,17250,17251],{"class":5478}," -plaintext",[5465,17253,17254],{"class":5503}," localhost:50051",[5465,17256,17257],{"class":5503}," list\n",[5465,17259,17260],{"class":5467,"line":5493},[5465,17261,5529],{"emptyLinePlaceholder":5528},[5465,17263,17264],{"class":5467,"line":5507},[5465,17265,17266],{"class":5471},"# Виклик методу\n",[5465,17268,17269,17271,17273],{"class":5467,"line":5519},[5465,17270,11624],{"class":5538},[5465,17272,17251],{"class":5478},[5465,17274,7882],{"class":7881},[5465,17276,17277,17280,17283],{"class":5467,"line":5525},[5465,17278,17279],{"class":5478},"  -d",[5465,17281,17282],{"class":5503}," '{\"name\": \"Alice\"}'",[5465,17284,7882],{"class":7881},[5465,17286,17287,17290],{"class":5467,"line":5532},[5465,17288,17289],{"class":5503},"  localhost:50051",[5465,17291,7882],{"class":7881},[5465,17293,17294],{"class":5467,"line":5565},[5465,17295,17296],{"class":5503},"  userservice.UserService\u002FCreateUser\n",[5280,17298,17299,6758,17301],{},[5232,17300,7435],{},[7379,17302,17161],{"href":17303,"rel":17304},"https:\u002F\u002Fgithub.com\u002Ffullstorydev\u002Fgrpcurl",[7383],[5193,17306,17309,17316,17321,17326,17340,17344,17621],{"icon":17307,"title":17308},"i-lucide-flask","🧪 Бібліотека bufconn",[5280,17310,17311,6758,17313],{},[5232,17312,7417],{},[5211,17314,17315],{},"google.golang.org\u002Fgrpc\u002Ftest\u002Fbufconn",[5280,17317,17318,17320],{},[5232,17319,7395],{}," In-memory мережевий listener для швидкого інтеграційного тестування gRPC",[5280,17322,17323],{},[5232,17324,17325],{},"Переваги:",[5198,17327,17328,17331,17334,17337],{},[5201,17329,17330],{},"Тести не вимагають відкриття реальних TCP-портів",[5201,17332,17333],{},"Усунення конфліктів портів у CI\u002FCD середовищах",[5201,17335,17336],{},"Набагато швидше за реальні мережеві з'єднання",[5201,17338,17339],{},"Ізоляція тестів один від одного",[5280,17341,17342],{},[5232,17343,9576],{},[5456,17345,17347],{"className":5458,"code":17346,"language":5460,"meta":5461,"style":5461},"import \"google.golang.org\u002Fgrpc\u002Ftest\u002Fbufconn\"\n\nconst bufSize = 1024 * 1024\n\nvar lis *bufconn.Listener\n\nfunc init() {\n    lis = bufconn.Listen(bufSize)\n    s := grpc.NewServer()\n    pb.RegisterUserServiceServer(s, &server{})\n    go s.Serve(lis)\n}\n\nfunc bufDialer(context.Context, string) (net.Conn, error) {\n    return lis.Dial()\n}\n\n\u002F\u002F У тестах\nconn, _ := grpc.DialContext(\n    ctx,\n    \"bufnet\",\n    grpc.WithContextDialer(bufDialer),\n    grpc.WithTransportCredentials(insecure.NewCredentials()),\n)\n",[5211,17348,17349,17356,17360,17378,17382,17400,17404,17413,17433,17447,17466,17484,17488,17492,17526,17538,17542,17546,17551,17570,17576,17583,17599,17617],{"__ignoreMap":5461},[5465,17350,17351,17353],{"class":5467,"line":5468},[5465,17352,9812],{"class":5675},[5465,17354,17355],{"class":5503}," \"google.golang.org\u002Fgrpc\u002Ftest\u002Fbufconn\"\n",[5465,17357,17358],{"class":5467,"line":5475},[5465,17359,5529],{"emptyLinePlaceholder":5528},[5465,17361,17362,17365,17369,17371,17373,17375],{"class":5467,"line":5493},[5465,17363,17364],{"class":5478},"const",[5465,17366,17368],{"class":17367},"s-QsJ"," bufSize",[5465,17370,14800],{"class":5489},[5465,17372,11391],{"class":7522},[5465,17374,11388],{"class":5489},[5465,17376,17377],{"class":7522},"1024\n",[5465,17379,17380],{"class":5467,"line":5507},[5465,17381,5529],{"emptyLinePlaceholder":5528},[5465,17383,17384,17387,17390,17392,17395,17397],{"class":5467,"line":5519},[5465,17385,17386],{"class":5478},"var",[5465,17388,17389],{"class":5496}," lis",[5465,17391,10122],{"class":5489},[5465,17393,17394],{"class":5482},"bufconn",[5465,17396,5214],{"class":5489},[5465,17398,17399],{"class":5482},"Listener\n",[5465,17401,17402],{"class":5467,"line":5525},[5465,17403,5529],{"emptyLinePlaceholder":5528},[5465,17405,17406,17408,17411],{"class":5467,"line":5532},[5465,17407,5535],{"class":5478},[5465,17409,17410],{"class":5538}," init",[5465,17412,11269],{"class":5489},[5465,17414,17415,17418,17420,17422,17424,17426,17428,17431],{"class":5467,"line":5565},[5465,17416,17417],{"class":5496},"    lis",[5465,17419,14800],{"class":5489},[5465,17421,17394],{"class":5496},[5465,17423,5214],{"class":5489},[5465,17425,11293],{"class":5538},[5465,17427,5542],{"class":5489},[5465,17429,17430],{"class":5496},"bufSize",[5465,17432,5591],{"class":5489},[5465,17434,17435,17437,17439,17441,17443,17445],{"class":5467,"line":5594},[5465,17436,10350],{"class":5496},[5465,17438,5576],{"class":5489},[5465,17440,7555],{"class":5496},[5465,17442,5214],{"class":5489},[5465,17444,11368],{"class":5538},[5465,17446,5724],{"class":5489},[5465,17448,17449,17451,17453,17455,17457,17459,17461,17463],{"class":5467,"line":5600},[5465,17450,9843],{"class":5496},[5465,17452,5214],{"class":5489},[5465,17454,11461],{"class":5538},[5465,17456,5542],{"class":5489},[5465,17458,10411],{"class":5496},[5465,17460,5920],{"class":5489},[5465,17462,17085],{"class":5482},[5465,17464,17465],{"class":5489},"{})\n",[5465,17467,17468,17471,17473,17475,17477,17479,17482],{"class":5467,"line":5624},[5465,17469,17470],{"class":5675},"    go",[5465,17472,10369],{"class":5496},[5465,17474,5214],{"class":5489},[5465,17476,11541],{"class":5538},[5465,17478,5542],{"class":5489},[5465,17480,17481],{"class":5496},"lis",[5465,17483,5591],{"class":5489},[5465,17485,17486],{"class":5467,"line":5639},[5465,17487,5522],{"class":5489},[5465,17489,17490],{"class":5467,"line":5647},[5465,17491,5529],{"emptyLinePlaceholder":5528},[5465,17493,17494,17496,17499,17501,17503,17505,17507,17509,17511,17513,17515,17517,17520,17522,17524],{"class":5467,"line":5666},[5465,17495,5535],{"class":5478},[5465,17497,17498],{"class":5538}," bufDialer",[5465,17500,5542],{"class":5489},[5465,17502,11969],{"class":5482},[5465,17504,5214],{"class":5489},[5465,17506,10112],{"class":5482},[5465,17508,5336],{"class":5489},[5465,17510,6764],{"class":5482},[5465,17512,13838],{"class":5489},[5465,17514,11288],{"class":5482},[5465,17516,5214],{"class":5489},[5465,17518,17519],{"class":5482},"Conn",[5465,17521,5336],{"class":5489},[5465,17523,5560],{"class":5482},[5465,17525,10148],{"class":5489},[5465,17527,17528,17530,17532,17534,17536],{"class":5467,"line":5672},[5465,17529,5844],{"class":5675},[5465,17531,17389],{"class":5496},[5465,17533,5214],{"class":5489},[5465,17535,11756],{"class":5538},[5465,17537,5724],{"class":5489},[5465,17539,17540],{"class":5467,"line":5690},[5465,17541,5522],{"class":5489},[5465,17543,17544],{"class":5467,"line":5699},[5465,17545,5529],{"emptyLinePlaceholder":5528},[5465,17547,17548],{"class":5467,"line":5705},[5465,17549,17550],{"class":5471},"\u002F\u002F У тестах\n",[5465,17552,17553,17555,17557,17559,17561,17563,17565,17568],{"class":5467,"line":5727},[5465,17554,11889],{"class":5496},[5465,17556,5336],{"class":5489},[5465,17558,5573],{"class":5496},[5465,17560,5576],{"class":5489},[5465,17562,7555],{"class":5496},[5465,17564,5214],{"class":5489},[5465,17566,17567],{"class":5538},"DialContext",[5465,17569,5621],{"class":5489},[5465,17571,17572,17574],{"class":5467,"line":5732},[5465,17573,10104],{"class":5496},[5465,17575,5636],{"class":5489},[5465,17577,17578,17581],{"class":5467,"line":5755},[5465,17579,17580],{"class":5503},"    \"bufnet\"",[5465,17582,5636],{"class":5489},[5465,17584,17585,17587,17589,17592,17594,17597],{"class":5467,"line":5786},[5465,17586,17100],{"class":5496},[5465,17588,5214],{"class":5489},[5465,17590,17591],{"class":5538},"WithContextDialer",[5465,17593,5542],{"class":5489},[5465,17595,17596],{"class":5496},"bufDialer",[5465,17598,5663],{"class":5489},[5465,17600,17601,17603,17605,17607,17609,17611,17613,17615],{"class":5467,"line":5831},[5465,17602,17100],{"class":5496},[5465,17604,5214],{"class":5489},[5465,17606,11774],{"class":5538},[5465,17608,5542],{"class":5489},[5465,17610,11779],{"class":5496},[5465,17612,5214],{"class":5489},[5465,17614,11784],{"class":5538},[5465,17616,15298],{"class":5489},[5465,17618,17619],{"class":5467,"line":5836},[5465,17620,5591],{"class":5489},[5280,17622,17623,6758,17625],{},[5232,17624,7435],{},[7379,17626,17629],{"href":17627,"rel":17628},"https:\u002F\u002Fpkg.go.dev\u002Fgoogle.golang.org\u002Fgrpc\u002Ftest\u002Fbufconn",[7383],"pkg.go.dev\u002Fgoogle.golang.org\u002Fgrpc\u002Ftest\u002Fbufconn",[5267,17631],{},[5270,17633,17635],{"id":17634},"тестування-та-налагодження-grpc-сервісів","Тестування та налагодження gRPC-сервісів",[5275,17637,17639],{"id":17638},"модульне-тестування-rpc-методів","Модульне тестування RPC-методів",[5280,17641,17642],{},"Оскільки RPC-обробники є звичайними Go-функціями, їх можна тестувати без запуску повноцінного gRPC-сервера, використовуючи мок-об'єкти.",[5456,17644,17646],{"className":5458,"code":17645,"language":5460,"meta":5461,"style":5461},"\u002F\u002F server\u002Fuser_service_test.go\npackage main\n\nimport (\n    \"context\"\n    \"testing\"\n    \n    pb \"github.com\u002Fexample\u002Fuserservice\u002Fpb\"\n    \"google.golang.org\u002Fgrpc\u002Fcodes\"\n    \"google.golang.org\u002Fgrpc\u002Fstatus\"\n)\n\nfunc TestCreateUser_Success(t *testing.T) {\n    server := NewUserServer()\n    \n    req := &pb.CreateUserRequest{\n        Name:  \"Alice\",\n        Email: \"alice@test.com\",\n    }\n    \n    resp, err := server.CreateUser(context.Background(), req)\n    \n    if err != nil {\n        t.Fatalf(\"Expected no error, got %v\", err)\n    }\n    \n    if resp.User.GetName() != \"Alice\" {\n        t.Errorf(\"Expected name 'Alice', got '%s'\", resp.User.GetName())\n    }\n    \n    if resp.User.GetId() == 0 {\n        t.Error(\"Expected non-zero user ID\")\n    }\n}\n\nfunc TestCreateUser_InvalidArgument(t *testing.T) {\n    server := NewUserServer()\n    \n    \u002F\u002F Запит без обов'язкового поля name\n    req := &pb.CreateUserRequest{\n        Email: \"test@example.com\",\n    }\n    \n    _, err := server.CreateUser(context.Background(), req)\n    \n    if err == nil {\n        t.Fatal(\"Expected error for empty name, got nil\")\n    }\n    \n    st, ok := status.FromError(err)\n    if !ok {\n        t.Fatal(\"Expected gRPC status error\")\n    }\n    \n    if st.Code() != codes.InvalidArgument {\n        t.Errorf(\"Expected InvalidArgument code, got %s\", st.Code())\n    }\n}\n",[5211,17647,17648,17653,17659,17663,17669,17673,17678,17682,17688,17692,17696,17700,17704,17728,17738,17742,17756,17767,17778,17782,17786,17816,17820,17832,17856,17860,17864,17885,17917,17921,17925,17945,17960,17964,17968,17972,17993,18003,18007,18012,18026,18037,18041,18045,18076,18080,18092,18107,18111,18115,18138,18148,18163,18167,18171,18191,18218,18222],{"__ignoreMap":5461},[5465,17649,17650],{"class":5467,"line":5468},[5465,17651,17652],{"class":5471},"\u002F\u002F server\u002Fuser_service_test.go\n",[5465,17654,17655,17657],{"class":5467,"line":5475},[5465,17656,9800],{"class":5478},[5465,17658,9803],{"class":5482},[5465,17660,17661],{"class":5467,"line":5493},[5465,17662,5529],{"emptyLinePlaceholder":5528},[5465,17664,17665,17667],{"class":5467,"line":5507},[5465,17666,9812],{"class":5675},[5465,17668,7535],{"class":5489},[5465,17670,17671],{"class":5467,"line":5519},[5465,17672,9819],{"class":5503},[5465,17674,17675],{"class":5467,"line":5525},[5465,17676,17677],{"class":5503},"    \"testing\"\n",[5465,17679,17680],{"class":5467,"line":5532},[5465,17681,5597],{"class":5489},[5465,17683,17684,17686],{"class":5467,"line":5565},[5465,17685,9843],{"class":5496},[5465,17687,9846],{"class":5503},[5465,17689,17690],{"class":5467,"line":5594},[5465,17691,9851],{"class":5503},[5465,17693,17694],{"class":5467,"line":5600},[5465,17695,9856],{"class":5503},[5465,17697,17698],{"class":5467,"line":5624},[5465,17699,5591],{"class":5489},[5465,17701,17702],{"class":5467,"line":5639},[5465,17703,5529],{"emptyLinePlaceholder":5528},[5465,17705,17706,17708,17711,17713,17716,17718,17721,17723,17726],{"class":5467,"line":5647},[5465,17707,5535],{"class":5478},[5465,17709,17710],{"class":5538}," TestCreateUser_Success",[5465,17712,5542],{"class":5489},[5465,17714,17715],{"class":5496},"t",[5465,17717,10122],{"class":5489},[5465,17719,17720],{"class":5482},"testing",[5465,17722,5214],{"class":5489},[5465,17724,17725],{"class":5482},"T",[5465,17727,10148],{"class":5489},[5465,17729,17730,17732,17734,17736],{"class":5467,"line":5666},[5465,17731,16148],{"class":5496},[5465,17733,5576],{"class":5489},[5465,17735,11450],{"class":5538},[5465,17737,5724],{"class":5489},[5465,17739,17740],{"class":5467,"line":5672},[5465,17741,5597],{"class":5489},[5465,17743,17744,17746,17748,17750,17752,17754],{"class":5467,"line":5690},[5465,17745,10119],{"class":5496},[5465,17747,10392],{"class":5489},[5465,17749,9928],{"class":5482},[5465,17751,5214],{"class":5489},[5465,17753,9007],{"class":5482},[5465,17755,5926],{"class":5489},[5465,17757,17758,17760,17762,17765],{"class":5467,"line":5699},[5465,17759,10423],{"class":5496},[5465,17761,5933],{"class":5489},[5465,17763,17764],{"class":5503},"\"Alice\"",[5465,17766,5636],{"class":5489},[5465,17768,17769,17771,17773,17776],{"class":5467,"line":5705},[5465,17770,10441],{"class":5496},[5465,17772,5807],{"class":5489},[5465,17774,17775],{"class":5503},"\"alice@test.com\"",[5465,17777,5636],{"class":5489},[5465,17779,17780],{"class":5467,"line":5727},[5465,17781,5702],{"class":5489},[5465,17783,17784],{"class":5467,"line":5732},[5465,17785,5597],{"class":5489},[5465,17787,17788,17790,17792,17794,17796,17798,17800,17802,17804,17806,17808,17810,17812,17814],{"class":5467,"line":5755},[5465,17789,5603],{"class":5496},[5465,17791,5336],{"class":5489},[5465,17793,5608],{"class":5496},[5465,17795,5576],{"class":5489},[5465,17797,17085],{"class":5496},[5465,17799,5214],{"class":5489},[5465,17801,5912],{"class":5538},[5465,17803,5542],{"class":5489},[5465,17805,11969],{"class":5496},[5465,17807,5214],{"class":5489},[5465,17809,11983],{"class":5538},[5465,17811,11986],{"class":5489},[5465,17813,10429],{"class":5496},[5465,17815,5591],{"class":5489},[5465,17817,17818],{"class":5467,"line":5786},[5465,17819,5597],{"class":5489},[5465,17821,17822,17824,17826,17828,17830],{"class":5467,"line":5831},[5465,17823,5676],{"class":5675},[5465,17825,5679],{"class":5496},[5465,17827,5682],{"class":5489},[5465,17829,5685],{"class":5478},[5465,17831,5490],{"class":5489},[5465,17833,17834,17837,17839,17841,17843,17846,17848,17850,17852,17854],{"class":5467,"line":5836},[5465,17835,17836],{"class":5496},"        t",[5465,17838,5214],{"class":5489},[5465,17840,11326],{"class":5538},[5465,17842,5542],{"class":5489},[5465,17844,17845],{"class":5503},"\"Expected no error, got ",[5465,17847,10887],{"class":5496},[5465,17849,5813],{"class":5503},[5465,17851,5336],{"class":5489},[5465,17853,5608],{"class":5496},[5465,17855,5591],{"class":5489},[5465,17857,17858],{"class":5467,"line":5841},[5465,17859,5702],{"class":5489},[5465,17861,17862],{"class":5467,"line":5850},[5465,17863,5597],{"class":5489},[5465,17865,17866,17868,17870,17872,17874,17876,17878,17881,17883],{"class":5467,"line":6213},[5465,17867,5676],{"class":5675},[5465,17869,5711],{"class":5496},[5465,17871,5214],{"class":5489},[5465,17873,5923],{"class":5496},[5465,17875,5214],{"class":5489},[5465,17877,10165],{"class":5538},[5465,17879,17880],{"class":5489},"() != ",[5465,17882,17764],{"class":5503},[5465,17884,5490],{"class":5489},[5465,17886,17887,17889,17891,17893,17895,17898,17900,17903,17905,17907,17909,17911,17913,17915],{"class":5467,"line":6218},[5465,17888,17836],{"class":5496},[5465,17890,5214],{"class":5489},[5465,17892,5796],{"class":5538},[5465,17894,5542],{"class":5489},[5465,17896,17897],{"class":5503},"\"Expected name 'Alice', got '",[5465,17899,5810],{"class":5496},[5465,17901,17902],{"class":5503},"'\"",[5465,17904,5336],{"class":5489},[5465,17906,5777],{"class":5496},[5465,17908,5214],{"class":5489},[5465,17910,5923],{"class":5496},[5465,17912,5214],{"class":5489},[5465,17914,10165],{"class":5538},[5465,17916,12206],{"class":5489},[5465,17918,17919],{"class":5467,"line":6224},[5465,17920,5702],{"class":5489},[5465,17922,17923],{"class":5467,"line":6230},[5465,17924,5597],{"class":5489},[5465,17926,17927,17929,17931,17933,17935,17937,17939,17941,17943],{"class":5467,"line":6236},[5465,17928,5676],{"class":5675},[5465,17930,5711],{"class":5496},[5465,17932,5214],{"class":5489},[5465,17934,5923],{"class":5496},[5465,17936,5214],{"class":5489},[5465,17938,12175],{"class":5538},[5465,17940,10168],{"class":5489},[5465,17942,6820],{"class":7522},[5465,17944,5490],{"class":5489},[5465,17946,17947,17949,17951,17953,17955,17958],{"class":5467,"line":6242},[5465,17948,17836],{"class":5496},[5465,17950,5214],{"class":5489},[5465,17952,10189],{"class":5538},[5465,17954,5542],{"class":5489},[5465,17956,17957],{"class":5503},"\"Expected non-zero user ID\"",[5465,17959,5591],{"class":5489},[5465,17961,17962],{"class":5467,"line":6248},[5465,17963,5702],{"class":5489},[5465,17965,17966],{"class":5467,"line":6254},[5465,17967,5522],{"class":5489},[5465,17969,17970],{"class":5467,"line":6260},[5465,17971,5529],{"emptyLinePlaceholder":5528},[5465,17973,17974,17976,17979,17981,17983,17985,17987,17989,17991],{"class":5467,"line":6265},[5465,17975,5535],{"class":5478},[5465,17977,17978],{"class":5538}," TestCreateUser_InvalidArgument",[5465,17980,5542],{"class":5489},[5465,17982,17715],{"class":5496},[5465,17984,10122],{"class":5489},[5465,17986,17720],{"class":5482},[5465,17988,5214],{"class":5489},[5465,17990,17725],{"class":5482},[5465,17992,10148],{"class":5489},[5465,17994,17995,17997,17999,18001],{"class":5467,"line":6270},[5465,17996,16148],{"class":5496},[5465,17998,5576],{"class":5489},[5465,18000,11450],{"class":5538},[5465,18002,5724],{"class":5489},[5465,18004,18005],{"class":5467,"line":6276},[5465,18006,5597],{"class":5489},[5465,18008,18009],{"class":5467,"line":6282},[5465,18010,18011],{"class":5471},"    \u002F\u002F Запит без обов'язкового поля name\n",[5465,18013,18014,18016,18018,18020,18022,18024],{"class":5467,"line":6288},[5465,18015,10119],{"class":5496},[5465,18017,10392],{"class":5489},[5465,18019,9928],{"class":5482},[5465,18021,5214],{"class":5489},[5465,18023,9007],{"class":5482},[5465,18025,5926],{"class":5489},[5465,18027,18028,18030,18032,18035],{"class":5467,"line":6294},[5465,18029,10441],{"class":5496},[5465,18031,5807],{"class":5489},[5465,18033,18034],{"class":5503},"\"test@example.com\"",[5465,18036,5636],{"class":5489},[5465,18038,18039],{"class":5467,"line":6300},[5465,18040,5702],{"class":5489},[5465,18042,18043],{"class":5467,"line":6306},[5465,18044,5597],{"class":5489},[5465,18046,18047,18050,18052,18054,18056,18058,18060,18062,18064,18066,18068,18070,18072,18074],{"class":5467,"line":6311},[5465,18048,18049],{"class":5496},"    _",[5465,18051,5336],{"class":5489},[5465,18053,5608],{"class":5496},[5465,18055,5576],{"class":5489},[5465,18057,17085],{"class":5496},[5465,18059,5214],{"class":5489},[5465,18061,5912],{"class":5538},[5465,18063,5542],{"class":5489},[5465,18065,11969],{"class":5496},[5465,18067,5214],{"class":5489},[5465,18069,11983],{"class":5538},[5465,18071,11986],{"class":5489},[5465,18073,10429],{"class":5496},[5465,18075,5591],{"class":5489},[5465,18077,18078],{"class":5467,"line":6316},[5465,18079,5597],{"class":5489},[5465,18081,18082,18084,18086,18088,18090],{"class":5467,"line":6322},[5465,18083,5676],{"class":5675},[5465,18085,5679],{"class":5496},[5465,18087,12453],{"class":5489},[5465,18089,5685],{"class":5478},[5465,18091,5490],{"class":5489},[5465,18093,18094,18096,18098,18100,18102,18105],{"class":5467,"line":6328},[5465,18095,17836],{"class":5496},[5465,18097,5214],{"class":5489},[5465,18099,16041],{"class":5538},[5465,18101,5542],{"class":5489},[5465,18103,18104],{"class":5503},"\"Expected error for empty name, got nil\"",[5465,18106,5591],{"class":5489},[5465,18108,18109],{"class":5467,"line":6334},[5465,18110,5702],{"class":5489},[5465,18112,18113],{"class":5467,"line":6340},[5465,18114,5597],{"class":5489},[5465,18116,18117,18120,18122,18124,18126,18128,18130,18132,18134,18136],{"class":5467,"line":6346},[5465,18118,18119],{"class":5496},"    st",[5465,18121,5336],{"class":5489},[5465,18123,13297],{"class":5496},[5465,18125,5576],{"class":5489},[5465,18127,10184],{"class":5496},[5465,18129,5214],{"class":5489},[5465,18131,13306],{"class":5538},[5465,18133,5542],{"class":5489},[5465,18135,5608],{"class":5496},[5465,18137,5591],{"class":5489},[5465,18139,18140,18142,18144,18146],{"class":5467,"line":6352},[5465,18141,5676],{"class":5675},[5465,18143,13319],{"class":5489},[5465,18145,13297],{"class":5496},[5465,18147,5490],{"class":5489},[5465,18149,18150,18152,18154,18156,18158,18161],{"class":5467,"line":6358},[5465,18151,17836],{"class":5496},[5465,18153,5214],{"class":5489},[5465,18155,16041],{"class":5538},[5465,18157,5542],{"class":5489},[5465,18159,18160],{"class":5503},"\"Expected gRPC status error\"",[5465,18162,5591],{"class":5489},[5465,18164,18165],{"class":5467,"line":6364},[5465,18166,5702],{"class":5489},[5465,18168,18169],{"class":5467,"line":6369},[5465,18170,5597],{"class":5489},[5465,18172,18173,18175,18177,18179,18181,18183,18185,18187,18189],{"class":5467,"line":6374},[5465,18174,5676],{"class":5675},[5465,18176,13372],{"class":5496},[5465,18178,5214],{"class":5489},[5465,18180,13377],{"class":5538},[5465,18182,17880],{"class":5489},[5465,18184,10194],{"class":5496},[5465,18186,5214],{"class":5489},[5465,18188,10199],{"class":5496},[5465,18190,5490],{"class":5489},[5465,18192,18193,18195,18197,18199,18201,18204,18206,18208,18210,18212,18214,18216],{"class":5467,"line":8905},[5465,18194,17836],{"class":5496},[5465,18196,5214],{"class":5489},[5465,18198,5796],{"class":5538},[5465,18200,5542],{"class":5489},[5465,18202,18203],{"class":5503},"\"Expected InvalidArgument code, got ",[5465,18205,5810],{"class":5496},[5465,18207,5813],{"class":5503},[5465,18209,5336],{"class":5489},[5465,18211,13415],{"class":5496},[5465,18213,5214],{"class":5489},[5465,18215,13377],{"class":5538},[5465,18217,12206],{"class":5489},[5465,18219,18220],{"class":5467,"line":8910},[5465,18221,5702],{"class":5489},[5465,18223,18224],{"class":5467,"line":8915},[5465,18225,5522],{"class":5489},[5275,18227,18229],{"id":18228},"інтеграційне-тестування-через-реальний-grpc-сервер","Інтеграційне тестування через реальний gRPC-сервер",[5280,18231,18232],{},"Для тестування повного циклу запит-відповідь, включно з інтерцепторами та серіалізацією, необхідно запустити тестовий gRPC-сервер.",[5456,18234,18236],{"className":5458,"code":18235,"language":5460,"meta":5461,"style":5461},"\u002F\u002F server\u002Fintegration_test.go\npackage main\n\nimport (\n    \"context\"\n    \"net\"\n    \"testing\"\n    \"time\"\n    \n    pb \"github.com\u002Fexample\u002Fuserservice\u002Fpb\"\n    \"google.golang.org\u002Fgrpc\"\n    \"google.golang.org\u002Fgrpc\u002Fcredentials\u002Finsecure\"\n    \"google.golang.org\u002Fgrpc\u002Ftest\u002Fbufconn\"\n)\n\nconst bufSize = 1024 * 1024\n\nvar lis *bufconn.Listener\n\nfunc init() {\n    \u002F\u002F Створюємо in-memory listener для тестування\n    lis = bufconn.Listen(bufSize)\n    \n    s := grpc.NewServer()\n    pb.RegisterUserServiceServer(s, NewUserServer())\n    \n    go func() {\n        if err := s.Serve(lis); err != nil {\n            panic(err)\n        }\n    }()\n}\n\nfunc bufDialer(context.Context, string) (net.Conn, error) {\n    return lis.Dial()\n}\n\nfunc TestIntegration_CreateAndGetUser(t *testing.T) {\n    ctx := context.Background()\n    \n    \u002F\u002F Підключаємось до тестового сервера\n    conn, err := grpc.DialContext(\n        ctx,\n        \"bufnet\",\n        grpc.WithContextDialer(bufDialer),\n        grpc.WithTransportCredentials(insecure.NewCredentials()),\n    )\n    if err != nil {\n        t.Fatalf(\"Failed to dial bufnet: %v\", err)\n    }\n    defer conn.Close()\n    \n    client := pb.NewUserServiceClient(conn)\n    \n    \u002F\u002F Створюємо користувача\n    createResp, err := client.CreateUser(ctx, &pb.CreateUserRequest{\n        Name:  \"Bob\",\n        Email: \"bob@test.com\",\n    })\n    if err != nil {\n        t.Fatalf(\"CreateUser failed: %v\", err)\n    }\n    \n    userID := createResp.User.GetId()\n    \n    \u002F\u002F Отримуємо створеного користувача\n    getResp, err := client.GetUser(ctx, &pb.GetUserRequest{\n        Id: userID,\n    })\n    if err != nil {\n        t.Fatalf(\"GetUser failed: %v\", err)\n    }\n    \n    if getResp.User.GetName() != \"Bob\" {\n        t.Errorf(\"Expected name 'Bob', got '%s'\", getResp.User.GetName())\n    }\n}\n",[5211,18237,18238,18243,18249,18253,18259,18263,18267,18271,18275,18279,18285,18289,18293,18298,18302,18306,18320,18324,18338,18342,18350,18355,18373,18377,18391,18409,18413,18421,18449,18460,18464,18469,18473,18477,18509,18521,18525,18529,18550,18564,18568,18573,18591,18597,18604,18618,18636,18640,18652,18675,18679,18691,18695,18713,18717,18722,18753,18764,18775,18779,18791,18813,18817,18821,18840,18844,18849,18880,18890,18894,18906,18929,18933,18937,18958,18990,18994],{"__ignoreMap":5461},[5465,18239,18240],{"class":5467,"line":5468},[5465,18241,18242],{"class":5471},"\u002F\u002F server\u002Fintegration_test.go\n",[5465,18244,18245,18247],{"class":5467,"line":5475},[5465,18246,9800],{"class":5478},[5465,18248,9803],{"class":5482},[5465,18250,18251],{"class":5467,"line":5493},[5465,18252,5529],{"emptyLinePlaceholder":5528},[5465,18254,18255,18257],{"class":5467,"line":5507},[5465,18256,9812],{"class":5675},[5465,18258,7535],{"class":5489},[5465,18260,18261],{"class":5467,"line":5519},[5465,18262,9819],{"class":5503},[5465,18264,18265],{"class":5467,"line":5525},[5465,18266,11231],{"class":5503},[5465,18268,18269],{"class":5467,"line":5532},[5465,18270,17677],{"class":5503},[5465,18272,18273],{"class":5467,"line":5565},[5465,18274,11698],{"class":5503},[5465,18276,18277],{"class":5467,"line":5594},[5465,18278,5597],{"class":5489},[5465,18280,18281,18283],{"class":5467,"line":5600},[5465,18282,9843],{"class":5496},[5465,18284,9846],{"class":5503},[5465,18286,18287],{"class":5467,"line":5624},[5465,18288,11246],{"class":5503},[5465,18290,18291],{"class":5467,"line":5639},[5465,18292,11717],{"class":5503},[5465,18294,18295],{"class":5467,"line":5647},[5465,18296,18297],{"class":5503},"    \"google.golang.org\u002Fgrpc\u002Ftest\u002Fbufconn\"\n",[5465,18299,18300],{"class":5467,"line":5666},[5465,18301,5591],{"class":5489},[5465,18303,18304],{"class":5467,"line":5672},[5465,18305,5529],{"emptyLinePlaceholder":5528},[5465,18307,18308,18310,18312,18314,18316,18318],{"class":5467,"line":5690},[5465,18309,17364],{"class":5478},[5465,18311,17368],{"class":17367},[5465,18313,14800],{"class":5489},[5465,18315,11391],{"class":7522},[5465,18317,11388],{"class":5489},[5465,18319,17377],{"class":7522},[5465,18321,18322],{"class":5467,"line":5699},[5465,18323,5529],{"emptyLinePlaceholder":5528},[5465,18325,18326,18328,18330,18332,18334,18336],{"class":5467,"line":5705},[5465,18327,17386],{"class":5478},[5465,18329,17389],{"class":5496},[5465,18331,10122],{"class":5489},[5465,18333,17394],{"class":5482},[5465,18335,5214],{"class":5489},[5465,18337,17399],{"class":5482},[5465,18339,18340],{"class":5467,"line":5727},[5465,18341,5529],{"emptyLinePlaceholder":5528},[5465,18343,18344,18346,18348],{"class":5467,"line":5732},[5465,18345,5535],{"class":5478},[5465,18347,17410],{"class":5538},[5465,18349,11269],{"class":5489},[5465,18351,18352],{"class":5467,"line":5755},[5465,18353,18354],{"class":5471},"    \u002F\u002F Створюємо in-memory listener для тестування\n",[5465,18356,18357,18359,18361,18363,18365,18367,18369,18371],{"class":5467,"line":5786},[5465,18358,17417],{"class":5496},[5465,18360,14800],{"class":5489},[5465,18362,17394],{"class":5496},[5465,18364,5214],{"class":5489},[5465,18366,11293],{"class":5538},[5465,18368,5542],{"class":5489},[5465,18370,17430],{"class":5496},[5465,18372,5591],{"class":5489},[5465,18374,18375],{"class":5467,"line":5831},[5465,18376,5597],{"class":5489},[5465,18378,18379,18381,18383,18385,18387,18389],{"class":5467,"line":5836},[5465,18380,10350],{"class":5496},[5465,18382,5576],{"class":5489},[5465,18384,7555],{"class":5496},[5465,18386,5214],{"class":5489},[5465,18388,11368],{"class":5538},[5465,18390,5724],{"class":5489},[5465,18392,18393,18395,18397,18399,18401,18403,18405,18407],{"class":5467,"line":5841},[5465,18394,9843],{"class":5496},[5465,18396,5214],{"class":5489},[5465,18398,11461],{"class":5538},[5465,18400,5542],{"class":5489},[5465,18402,10411],{"class":5496},[5465,18404,5336],{"class":5489},[5465,18406,11450],{"class":5538},[5465,18408,12206],{"class":5489},[5465,18410,18411],{"class":5467,"line":5850},[5465,18412,5597],{"class":5489},[5465,18414,18415,18417,18419],{"class":5467,"line":6213},[5465,18416,17470],{"class":5675},[5465,18418,15724],{"class":5478},[5465,18420,11269],{"class":5489},[5465,18422,18423,18425,18427,18429,18431,18433,18435,18437,18439,18441,18443,18445,18447],{"class":5467,"line":6218},[5465,18424,10843],{"class":5675},[5465,18426,5679],{"class":5496},[5465,18428,5576],{"class":5489},[5465,18430,10411],{"class":5496},[5465,18432,5214],{"class":5489},[5465,18434,11541],{"class":5538},[5465,18436,5542],{"class":5489},[5465,18438,17481],{"class":5496},[5465,18440,10960],{"class":5489},[5465,18442,5608],{"class":5496},[5465,18444,5682],{"class":5489},[5465,18446,5685],{"class":5478},[5465,18448,5490],{"class":5489},[5465,18450,18451,18454,18456,18458],{"class":5467,"line":6224},[5465,18452,18453],{"class":5538},"            panic",[5465,18455,5542],{"class":5489},[5465,18457,5608],{"class":5496},[5465,18459,5591],{"class":5489},[5465,18461,18462],{"class":5467,"line":6230},[5465,18463,10928],{"class":5489},[5465,18465,18466],{"class":5467,"line":6236},[5465,18467,18468],{"class":5489},"    }()\n",[5465,18470,18471],{"class":5467,"line":6242},[5465,18472,5522],{"class":5489},[5465,18474,18475],{"class":5467,"line":6248},[5465,18476,5529],{"emptyLinePlaceholder":5528},[5465,18478,18479,18481,18483,18485,18487,18489,18491,18493,18495,18497,18499,18501,18503,18505,18507],{"class":5467,"line":6254},[5465,18480,5535],{"class":5478},[5465,18482,17498],{"class":5538},[5465,18484,5542],{"class":5489},[5465,18486,11969],{"class":5482},[5465,18488,5214],{"class":5489},[5465,18490,10112],{"class":5482},[5465,18492,5336],{"class":5489},[5465,18494,6764],{"class":5482},[5465,18496,13838],{"class":5489},[5465,18498,11288],{"class":5482},[5465,18500,5214],{"class":5489},[5465,18502,17519],{"class":5482},[5465,18504,5336],{"class":5489},[5465,18506,5560],{"class":5482},[5465,18508,10148],{"class":5489},[5465,18510,18511,18513,18515,18517,18519],{"class":5467,"line":6260},[5465,18512,5844],{"class":5675},[5465,18514,17389],{"class":5496},[5465,18516,5214],{"class":5489},[5465,18518,11756],{"class":5538},[5465,18520,5724],{"class":5489},[5465,18522,18523],{"class":5467,"line":6265},[5465,18524,5522],{"class":5489},[5465,18526,18527],{"class":5467,"line":6270},[5465,18528,5529],{"emptyLinePlaceholder":5528},[5465,18530,18531,18533,18536,18538,18540,18542,18544,18546,18548],{"class":5467,"line":6276},[5465,18532,5535],{"class":5478},[5465,18534,18535],{"class":5538}," TestIntegration_CreateAndGetUser",[5465,18537,5542],{"class":5489},[5465,18539,17715],{"class":5496},[5465,18541,10122],{"class":5489},[5465,18543,17720],{"class":5482},[5465,18545,5214],{"class":5489},[5465,18547,17725],{"class":5482},[5465,18549,10148],{"class":5489},[5465,18551,18552,18554,18556,18558,18560,18562],{"class":5467,"line":6282},[5465,18553,10104],{"class":5496},[5465,18555,5576],{"class":5489},[5465,18557,11969],{"class":5496},[5465,18559,5214],{"class":5489},[5465,18561,11983],{"class":5538},[5465,18563,5724],{"class":5489},[5465,18565,18566],{"class":5467,"line":6288},[5465,18567,5597],{"class":5489},[5465,18569,18570],{"class":5467,"line":6294},[5465,18571,18572],{"class":5471},"    \u002F\u002F Підключаємось до тестового сервера\n",[5465,18574,18575,18577,18579,18581,18583,18585,18587,18589],{"class":5467,"line":6300},[5465,18576,11743],{"class":5496},[5465,18578,5336],{"class":5489},[5465,18580,5608],{"class":5496},[5465,18582,5576],{"class":5489},[5465,18584,7555],{"class":5496},[5465,18586,5214],{"class":5489},[5465,18588,17567],{"class":5538},[5465,18590,5621],{"class":5489},[5465,18592,18593,18595],{"class":5467,"line":6306},[5465,18594,15857],{"class":5496},[5465,18596,5636],{"class":5489},[5465,18598,18599,18602],{"class":5467,"line":6311},[5465,18600,18601],{"class":5503},"        \"bufnet\"",[5465,18603,5636],{"class":5489},[5465,18605,18606,18608,18610,18612,18614,18616],{"class":5467,"line":6316},[5465,18607,11375],{"class":5496},[5465,18609,5214],{"class":5489},[5465,18611,17591],{"class":5538},[5465,18613,5542],{"class":5489},[5465,18615,17596],{"class":5496},[5465,18617,5663],{"class":5489},[5465,18619,18620,18622,18624,18626,18628,18630,18632,18634],{"class":5467,"line":6322},[5465,18621,11375],{"class":5496},[5465,18623,5214],{"class":5489},[5465,18625,11774],{"class":5538},[5465,18627,5542],{"class":5489},[5465,18629,11779],{"class":5496},[5465,18631,5214],{"class":5489},[5465,18633,11784],{"class":5538},[5465,18635,15298],{"class":5489},[5465,18637,18638],{"class":5467,"line":6328},[5465,18639,5669],{"class":5489},[5465,18641,18642,18644,18646,18648,18650],{"class":5467,"line":6334},[5465,18643,5676],{"class":5675},[5465,18645,5679],{"class":5496},[5465,18647,5682],{"class":5489},[5465,18649,5685],{"class":5478},[5465,18651,5490],{"class":5489},[5465,18653,18654,18656,18658,18660,18662,18665,18667,18669,18671,18673],{"class":5467,"line":6340},[5465,18655,17836],{"class":5496},[5465,18657,5214],{"class":5489},[5465,18659,11326],{"class":5538},[5465,18661,5542],{"class":5489},[5465,18663,18664],{"class":5503},"\"Failed to dial bufnet: ",[5465,18666,10887],{"class":5496},[5465,18668,5813],{"class":5503},[5465,18670,5336],{"class":5489},[5465,18672,5608],{"class":5496},[5465,18674,5591],{"class":5489},[5465,18676,18677],{"class":5467,"line":6346},[5465,18678,5702],{"class":5489},[5465,18680,18681,18683,18685,18687,18689],{"class":5467,"line":6352},[5465,18682,5708],{"class":5675},[5465,18684,11855],{"class":5496},[5465,18686,5214],{"class":5489},[5465,18688,5721],{"class":5538},[5465,18690,5724],{"class":5489},[5465,18692,18693],{"class":5467,"line":6358},[5465,18694,5597],{"class":5489},[5465,18696,18697,18699,18701,18703,18705,18707,18709,18711],{"class":5467,"line":6364},[5465,18698,11875],{"class":5496},[5465,18700,5576],{"class":5489},[5465,18702,9928],{"class":5496},[5465,18704,5214],{"class":5489},[5465,18706,11884],{"class":5538},[5465,18708,5542],{"class":5489},[5465,18710,11889],{"class":5496},[5465,18712,5591],{"class":5489},[5465,18714,18715],{"class":5467,"line":6369},[5465,18716,5597],{"class":5489},[5465,18718,18719],{"class":5467,"line":6374},[5465,18720,18721],{"class":5471},"    \u002F\u002F Створюємо користувача\n",[5465,18723,18724,18727,18729,18731,18733,18735,18737,18739,18741,18743,18745,18747,18749,18751],{"class":5467,"line":8905},[5465,18725,18726],{"class":5496},"    createResp",[5465,18728,5336],{"class":5489},[5465,18730,5608],{"class":5496},[5465,18732,5576],{"class":5489},[5465,18734,5878],{"class":5496},[5465,18736,5214],{"class":5489},[5465,18738,5912],{"class":5538},[5465,18740,5542],{"class":5489},[5465,18742,5917],{"class":5496},[5465,18744,5920],{"class":5489},[5465,18746,9928],{"class":5482},[5465,18748,5214],{"class":5489},[5465,18750,9007],{"class":5482},[5465,18752,5926],{"class":5489},[5465,18754,18755,18757,18759,18762],{"class":5467,"line":8910},[5465,18756,10423],{"class":5496},[5465,18758,5933],{"class":5489},[5465,18760,18761],{"class":5503},"\"Bob\"",[5465,18763,5636],{"class":5489},[5465,18765,18766,18768,18770,18773],{"class":5467,"line":8915},[5465,18767,10441],{"class":5496},[5465,18769,5807],{"class":5489},[5465,18771,18772],{"class":5503},"\"bob@test.com\"",[5465,18774,5636],{"class":5489},[5465,18776,18777],{"class":5467,"line":8921},[5465,18778,12088],{"class":5489},[5465,18780,18781,18783,18785,18787,18789],{"class":5467,"line":8927},[5465,18782,5676],{"class":5675},[5465,18784,5679],{"class":5496},[5465,18786,5682],{"class":5489},[5465,18788,5685],{"class":5478},[5465,18790,5490],{"class":5489},[5465,18792,18793,18795,18797,18799,18801,18803,18805,18807,18809,18811],{"class":5467,"line":8933},[5465,18794,17836],{"class":5496},[5465,18796,5214],{"class":5489},[5465,18798,11326],{"class":5538},[5465,18800,5542],{"class":5489},[5465,18802,12113],{"class":5503},[5465,18804,10887],{"class":5496},[5465,18806,5813],{"class":5503},[5465,18808,5336],{"class":5489},[5465,18810,5608],{"class":5496},[5465,18812,5591],{"class":5489},[5465,18814,18815],{"class":5467,"line":8938},[5465,18816,5702],{"class":5489},[5465,18818,18819],{"class":5467,"line":8943},[5465,18820,5597],{"class":5489},[5465,18822,18823,18825,18827,18830,18832,18834,18836,18838],{"class":5467,"line":8949},[5465,18824,14723],{"class":5496},[5465,18826,5576],{"class":5489},[5465,18828,18829],{"class":5496},"createResp",[5465,18831,5214],{"class":5489},[5465,18833,5923],{"class":5496},[5465,18835,5214],{"class":5489},[5465,18837,12175],{"class":5538},[5465,18839,5724],{"class":5489},[5465,18841,18842],{"class":5467,"line":8955},[5465,18843,5597],{"class":5489},[5465,18845,18846],{"class":5467,"line":8961},[5465,18847,18848],{"class":5471},"    \u002F\u002F Отримуємо створеного користувача\n",[5465,18850,18851,18854,18856,18858,18860,18862,18864,18866,18868,18870,18872,18874,18876,18878],{"class":5467,"line":8967},[5465,18852,18853],{"class":5496},"    getResp",[5465,18855,5336],{"class":5489},[5465,18857,5608],{"class":5496},[5465,18859,5576],{"class":5489},[5465,18861,5878],{"class":5496},[5465,18863,5214],{"class":5489},[5465,18865,8998],{"class":5538},[5465,18867,5542],{"class":5489},[5465,18869,5917],{"class":5496},[5465,18871,5920],{"class":5489},[5465,18873,9928],{"class":5482},[5465,18875,5214],{"class":5489},[5465,18877,6836],{"class":5482},[5465,18879,5926],{"class":5489},[5465,18881,18882,18884,18886,18888],{"class":5467,"line":9406},[5465,18883,10405],{"class":5496},[5465,18885,5807],{"class":5489},[5465,18887,14821],{"class":5496},[5465,18889,5636],{"class":5489},[5465,18891,18892],{"class":5467,"line":9412},[5465,18893,12088],{"class":5489},[5465,18895,18896,18898,18900,18902,18904],{"class":5467,"line":9418},[5465,18897,5676],{"class":5675},[5465,18899,5679],{"class":5496},[5465,18901,5682],{"class":5489},[5465,18903,5685],{"class":5478},[5465,18905,5490],{"class":5489},[5465,18907,18908,18910,18912,18914,18916,18919,18921,18923,18925,18927],{"class":5467,"line":9423},[5465,18909,17836],{"class":5496},[5465,18911,5214],{"class":5489},[5465,18913,11326],{"class":5538},[5465,18915,5542],{"class":5489},[5465,18917,18918],{"class":5503},"\"GetUser failed: ",[5465,18920,10887],{"class":5496},[5465,18922,5813],{"class":5503},[5465,18924,5336],{"class":5489},[5465,18926,5608],{"class":5496},[5465,18928,5591],{"class":5489},[5465,18930,18931],{"class":5467,"line":9428},[5465,18932,5702],{"class":5489},[5465,18934,18935],{"class":5467,"line":9434},[5465,18936,5597],{"class":5489},[5465,18938,18939,18941,18944,18946,18948,18950,18952,18954,18956],{"class":5467,"line":9439},[5465,18940,5676],{"class":5675},[5465,18942,18943],{"class":5496}," getResp",[5465,18945,5214],{"class":5489},[5465,18947,5923],{"class":5496},[5465,18949,5214],{"class":5489},[5465,18951,10165],{"class":5538},[5465,18953,17880],{"class":5489},[5465,18955,18761],{"class":5503},[5465,18957,5490],{"class":5489},[5465,18959,18960,18962,18964,18966,18968,18971,18973,18975,18977,18980,18982,18984,18986,18988],{"class":5467,"line":9445},[5465,18961,17836],{"class":5496},[5465,18963,5214],{"class":5489},[5465,18965,5796],{"class":5538},[5465,18967,5542],{"class":5489},[5465,18969,18970],{"class":5503},"\"Expected name 'Bob', got '",[5465,18972,5810],{"class":5496},[5465,18974,17902],{"class":5503},[5465,18976,5336],{"class":5489},[5465,18978,18979],{"class":5496},"getResp",[5465,18981,5214],{"class":5489},[5465,18983,5923],{"class":5496},[5465,18985,5214],{"class":5489},[5465,18987,10165],{"class":5538},[5465,18989,12206],{"class":5489},[5465,18991,18992],{"class":5467,"line":9451},[5465,18993,5702],{"class":5489},[5465,18995,18996],{"class":5467,"line":9456},[5465,18997,5522],{"class":5489},[5280,18999,19000,10055,19003,19006],{},[5232,19001,19002],{},"Рядки 19–31:",[5211,19004,19005],{},"bufconn.Listener"," — спеціальний in-memory listener, що дозволяє тестувати gRPC без відкриття реальних TCP-портів. Це робить тести швидшими та ізольованими.",[6839,19008,19009,19010,19013,19014,5214],{},"Для тестування інтерцепторів автентифікації та авторизації використовуйте бібліотеку ",[5211,19011,19012],{},"grpc-testing"," або створюйте тестові контексти з попередньо заповненими метаданими через ",[5211,19015,19016],{},"metadata.NewIncomingContext()",[5275,19018,19020],{"id":19019},"налагодження-через-grpcurl","Налагодження через grpcurl",[5280,19022,19023,19027,19028,19030],{},[5232,19024,19025],{},[5211,19026,11624],{}," — це інструмент командного рядка для ручного тестування gRPC-сервісів (аналог ",[5211,19029,17169],{}," для HTTP). Працює через gRPC Reflection API.",[7641,19032,19033,19048],{},[7644,19034,19035],{"label":7646},[5456,19036,19038],{"className":7649,"code":19037,"language":7651,"meta":5461,"style":5461},"brew install grpcurl\n",[5211,19039,19040],{"__ignoreMap":5461},[5465,19041,19042,19044,19046],{"class":5467,"line":5468},[5465,19043,7663],{"class":5538},[5465,19045,7666],{"class":5503},[5465,19047,17193],{"class":5503},[7644,19049,19051],{"label":19050},"Go Install",[5456,19052,19054],{"className":7649,"code":19053,"language":7651,"meta":5461,"style":5461},"go install github.com\u002Ffullstorydev\u002Fgrpcurl\u002Fcmd\u002Fgrpcurl@latest\n",[5211,19055,19056],{"__ignoreMap":5461},[5465,19057,19058,19060,19062],{"class":5467,"line":5468},[5465,19059,5460],{"class":5538},[5465,19061,7666],{"class":5503},[5465,19063,17211],{"class":5503},[5280,19065,19066],{},[5232,19067,19068],{},"Приклади використання:",[5456,19070,19072],{"className":7649,"code":19071,"language":7651,"meta":5461,"style":5461},"# Список доступних сервісів\ngrpcurl -plaintext localhost:50051 list\n\n# Список методів конкретного сервісу\ngrpcurl -plaintext localhost:50051 list userservice.UserService\n\n# Опис методу (типи запиту та відповіді)\ngrpcurl -plaintext localhost:50051 describe userservice.UserService.CreateUser\n\n# Виклик Unary RPC методу\ngrpcurl -plaintext \\\n  -d '{\"name\": \"Charlie\", \"email\": \"charlie@example.com\"}' \\\n  localhost:50051 \\\n  userservice.UserService\u002FCreateUser\n\n# Виклик із метаданими (JWT токен)\ngrpcurl -plaintext \\\n  -H \"Authorization: Bearer valid_token_123\" \\\n  -d '{\"id\": 1}' \\\n  localhost:50051 \\\n  userservice.UserService\u002FGetUser\n",[5211,19073,19074,19079,19089,19093,19098,19112,19116,19121,19135,19139,19144,19152,19161,19167,19171,19175,19180,19188,19198,19207,19213],{"__ignoreMap":5461},[5465,19075,19076],{"class":5467,"line":5468},[5465,19077,19078],{"class":5471},"# Список доступних сервісів\n",[5465,19080,19081,19083,19085,19087],{"class":5467,"line":5475},[5465,19082,11624],{"class":5538},[5465,19084,17251],{"class":5478},[5465,19086,17254],{"class":5503},[5465,19088,17257],{"class":5503},[5465,19090,19091],{"class":5467,"line":5493},[5465,19092,5529],{"emptyLinePlaceholder":5528},[5465,19094,19095],{"class":5467,"line":5507},[5465,19096,19097],{"class":5471},"# Список методів конкретного сервісу\n",[5465,19099,19100,19102,19104,19106,19109],{"class":5467,"line":5519},[5465,19101,11624],{"class":5538},[5465,19103,17251],{"class":5478},[5465,19105,17254],{"class":5503},[5465,19107,19108],{"class":5503}," list",[5465,19110,19111],{"class":5503}," userservice.UserService\n",[5465,19113,19114],{"class":5467,"line":5525},[5465,19115,5529],{"emptyLinePlaceholder":5528},[5465,19117,19118],{"class":5467,"line":5532},[5465,19119,19120],{"class":5471},"# Опис методу (типи запиту та відповіді)\n",[5465,19122,19123,19125,19127,19129,19132],{"class":5467,"line":5565},[5465,19124,11624],{"class":5538},[5465,19126,17251],{"class":5478},[5465,19128,17254],{"class":5503},[5465,19130,19131],{"class":5503}," describe",[5465,19133,19134],{"class":5503}," userservice.UserService.CreateUser\n",[5465,19136,19137],{"class":5467,"line":5594},[5465,19138,5529],{"emptyLinePlaceholder":5528},[5465,19140,19141],{"class":5467,"line":5600},[5465,19142,19143],{"class":5471},"# Виклик Unary RPC методу\n",[5465,19145,19146,19148,19150],{"class":5467,"line":5624},[5465,19147,11624],{"class":5538},[5465,19149,17251],{"class":5478},[5465,19151,7882],{"class":7881},[5465,19153,19154,19156,19159],{"class":5467,"line":5639},[5465,19155,17279],{"class":5478},[5465,19157,19158],{"class":5503}," '{\"name\": \"Charlie\", \"email\": \"charlie@example.com\"}'",[5465,19160,7882],{"class":7881},[5465,19162,19163,19165],{"class":5467,"line":5647},[5465,19164,17289],{"class":5503},[5465,19166,7882],{"class":7881},[5465,19168,19169],{"class":5467,"line":5666},[5465,19170,17296],{"class":5503},[5465,19172,19173],{"class":5467,"line":5672},[5465,19174,5529],{"emptyLinePlaceholder":5528},[5465,19176,19177],{"class":5467,"line":5690},[5465,19178,19179],{"class":5471},"# Виклик із метаданими (JWT токен)\n",[5465,19181,19182,19184,19186],{"class":5467,"line":5699},[5465,19183,11624],{"class":5538},[5465,19185,17251],{"class":5478},[5465,19187,7882],{"class":7881},[5465,19189,19190,19193,19196],{"class":5467,"line":5705},[5465,19191,19192],{"class":5478},"  -H",[5465,19194,19195],{"class":5503}," \"Authorization: Bearer valid_token_123\"",[5465,19197,7882],{"class":7881},[5465,19199,19200,19202,19205],{"class":5467,"line":5727},[5465,19201,17279],{"class":5478},[5465,19203,19204],{"class":5503}," '{\"id\": 1}'",[5465,19206,7882],{"class":7881},[5465,19208,19209,19211],{"class":5467,"line":5732},[5465,19210,17289],{"class":5503},[5465,19212,7882],{"class":7881},[5465,19214,19215],{"class":5467,"line":5755},[5465,19216,19217],{"class":5503},"  userservice.UserService\u002FGetUser\n",[5280,19219,19220,19226,19227,19230,19231,7048,19234,5214],{},[5232,19221,19222,19223,8158],{},"Опція ",[5211,19224,19225],{},"-plaintext"," Вимикає TLS (для локальної розробки). У продакшні використовуйте ",[5211,19228,19229],{},"-insecure"," або надайте TLS-сертифікати через ",[5211,19232,19233],{},"-cert",[5211,19235,19236],{},"-key",[5430,19238,19239,19240,19242,19243,19246],{},"Для роботи ",[5211,19241,11624],{}," необхідно, щоб на сервері було увімкнено gRPC Reflection через ",[5211,19244,19245],{},"reflection.Register(grpcServer)",". У продакшн-середовищі рекомендується вимикати Reflection з міркувань безпеки або обмежити доступ через автентифікацію.",[5275,19248,19250],{"id":19249},"додаткові-інструменти-для-роботи-з-grpc","Додаткові інструменти для роботи з gRPC",[5190,19252,19253,19341,19536],{},[5193,19254,19257,19262,19267,19296,19301,19315,19320],{"icon":19255,"title":19256},"i-lucide-paint-bucket","🎨 BloomRPC \u002F Postman gRPC",[5280,19258,19259],{},[5232,19260,19261],{},"GUI клієнти для тестування gRPC API",[5280,19263,19264],{},[5232,19265,19266],{},"BloomRPC (Desktop)",[5198,19268,19269,19278,19281,19287,19290],{},[5201,19270,19271,6758,19273],{},[5232,19272,7377],{},[7379,19274,19277],{"href":19275,"rel":19276},"https:\u002F\u002Fgithub.com\u002Fbloomrpc\u002Fbloomrpc",[7383],"github.com\u002Fbloomrpc\u002Fbloomrpc",[5201,19279,19280],{},"Легкий Electron-додаток для ручного тестування gRPC",[5201,19282,19283,19284,19286],{},"Підтримка ",[5211,19285,5213],{}," файлів або gRPC Reflection",[5201,19288,19289],{},"Збереження історії запитів та колекцій",[5201,19291,19292,19295],{},[5232,19293,19294],{},"Статус:"," Архівний проєкт, але все ще працює",[5280,19297,19298],{},[5232,19299,19300],{},"Postman (з підтримкою gRPC)",[5198,19302,19303,19306,19309,19312],{},[5201,19304,19305],{},"Починаючи з версії 9.7 підтримує gRPC",[5201,19307,19308],{},"Інтеграція у існуючі Postman колекції",[5201,19310,19311],{},"Підтримка Server Streaming, Client Streaming, Bidirectional Streaming",[5201,19313,19314],{},"Збереження environment змінних",[5280,19316,19317],{},[5232,19318,19319],{},"Kreya (Desktop)",[5198,19321,19322,19332,19335,19338],{},[5201,19323,19324,6758,19327],{},[5232,19325,19326],{},"Сайт:",[7379,19328,19331],{"href":19329,"rel":19330},"https:\u002F\u002Fkreya.app",[7383],"kreya.app",[5201,19333,19334],{},"Сучасна альтернатива BloomRPC",[5201,19336,19337],{},"Підтримка gRPC, REST та GraphQL",[5201,19339,19340],{},"Доступний для Windows, macOS, Linux",[5193,19342,19345,19352,19359,19363,19368,19382,19387,19401,19406,19419,19423,19458,19467,19527],{"icon":19343,"title":19344},"i-lucide-package-check","🛠️ buf — Modern Protobuf Toolchain",[5280,19346,19347,6758,19349],{},[5232,19348,7417],{},[5211,19350,19351],{},"buf.build",[5280,19353,19354,19356,19357],{},[5232,19355,7395],{}," Сучасний інструмент для роботи з Protocol Buffers, що замінює ",[5211,19358,6435],{},[5280,19360,19361],{},[5232,19362,15046],{},[5280,19364,19365],{},[5232,19366,19367],{},"Linting та Breaking Change Detection:",[5198,19369,19370,19376,19379],{},[5201,19371,19372,19373,19375],{},"Автоматична перевірка ",[5211,19374,5213],{}," файлів на відповідність best practices",[5201,19377,19378],{},"Виявлення breaking changes між версіями API",[5201,19380,19381],{},"Інтеграція у CI\u002FCD пайплайни",[5280,19383,19384],{},[5232,19385,19386],{},"Швидка компіляція:",[5198,19388,19389,19395,19398],{},[5201,19390,19391,19392,19394],{},"Оптимізована компіляція ",[5211,19393,5213],{}," файлів",[5201,19396,19397],{},"Паралельна обробка залежностей",[5201,19399,19400],{},"Кешування результатів",[5280,19402,19403],{},[5232,19404,19405],{},"Buf Schema Registry (BSR):",[5198,19407,19408,19413,19416],{},[5201,19409,19410,19411,19394],{},"Централізоване сховище ",[5211,19412,5213],{},[5201,19414,19415],{},"Версіювання схем API",[5201,19417,19418],{},"Публікація та споживання схем як пакетів",[5280,19420,19421],{},[5232,19422,15065],{},[5456,19424,19426],{"className":7649,"code":19425,"language":7651,"meta":5461,"style":5461},"# macOS\nbrew install bufbuild\u002Fbuf\u002Fbuf\n\n# Go Install\ngo install github.com\u002Fbufbuild\u002Fbuf\u002Fcmd\u002Fbuf@latest\n",[5211,19427,19428,19432,19441,19445,19449],{"__ignoreMap":5461},[5465,19429,19430],{"class":5467,"line":5468},[5465,19431,17184],{"class":5471},[5465,19433,19434,19436,19438],{"class":5467,"line":5475},[5465,19435,7663],{"class":5538},[5465,19437,7666],{"class":5503},[5465,19439,19440],{"class":5503}," bufbuild\u002Fbuf\u002Fbuf\n",[5465,19442,19443],{"class":5467,"line":5493},[5465,19444,5529],{"emptyLinePlaceholder":5528},[5465,19446,19447],{"class":5467,"line":5507},[5465,19448,17202],{"class":5471},[5465,19450,19451,19453,19455],{"class":5467,"line":5519},[5465,19452,5460],{"class":5538},[5465,19454,7666],{"class":5503},[5465,19456,19457],{"class":5503}," github.com\u002Fbufbuild\u002Fbuf\u002Fcmd\u002Fbuf@latest\n",[5280,19459,19460],{},[5232,19461,19462,19463,19466],{},"Приклад конфігурації (",[5211,19464,19465],{},"buf.yaml","):",[5456,19468,19472],{"className":19469,"code":19470,"language":19471,"meta":5461,"style":5461},"language-yaml shiki shiki-themes light-plus dark-plus dark-plus","version: v1\nbreaking:\n  use:\n    - FILE\nlint:\n  use:\n    - DEFAULT\n","yaml",[5211,19473,19474,19485,19492,19499,19507,19514,19520],{"__ignoreMap":5461},[5465,19475,19476,19479,19481],{"class":5467,"line":5468},[5465,19477,19478],{"class":8155},"version",[5465,19480,5807],{"class":5489},[5465,19482,19484],{"class":19483},"su9tN","v1\n",[5465,19486,19487,19490],{"class":5467,"line":5475},[5465,19488,19489],{"class":8155},"breaking",[5465,19491,10332],{"class":5489},[5465,19493,19494,19497],{"class":5467,"line":5493},[5465,19495,19496],{"class":8155},"  use",[5465,19498,10332],{"class":5489},[5465,19500,19501,19504],{"class":5467,"line":5507},[5465,19502,19503],{"class":5489},"    - ",[5465,19505,19506],{"class":19483},"FILE\n",[5465,19508,19509,19512],{"class":5467,"line":5519},[5465,19510,19511],{"class":8155},"lint",[5465,19513,10332],{"class":5489},[5465,19515,19516,19518],{"class":5467,"line":5525},[5465,19517,19496],{"class":8155},[5465,19519,10332],{"class":5489},[5465,19521,19522,19524],{"class":5467,"line":5532},[5465,19523,19503],{"class":5489},[5465,19525,19526],{"class":19483},"DEFAULT\n",[5280,19528,19529,6758,19531],{},[5232,19530,7435],{},[7379,19532,19535],{"href":19533,"rel":19534},"https:\u002F\u002Fbuf.build\u002Fdocs",[7383],"buf.build\u002Fdocs",[5193,19537,19540,19547,19552,19557,19571,19575,19610,19615,19677],{"icon":19538,"title":19539},"i-lucide-code","🔐 Evans — gRPC REPL Client",[5280,19541,19542,6758,19544],{},[5232,19543,7377],{},[5211,19545,19546],{},"github.com\u002Fktr0731\u002Fevans",[5280,19548,19549,19551],{},[5232,19550,7395],{}," Інтерактивний REPL (Read-Eval-Print Loop) клієнт для gRPC",[5280,19553,19554],{},[5232,19555,19556],{},"Особливості:",[5198,19558,19559,19562,19565,19568],{},[5201,19560,19561],{},"Інтерактивний режим із автодоповненням",[5201,19563,19564],{},"Підтримка gRPC Reflection",[5201,19566,19567],{},"CLI режим для скриптування",[5201,19569,19570],{},"Підтримка TLS та метаданих",[5280,19572,19573],{},[5232,19574,15065],{},[5456,19576,19578],{"className":7649,"code":19577,"language":7651,"meta":5461,"style":5461},"# macOS\nbrew install evans\n\n# Go Install\ngo install github.com\u002Fktr0731\u002Fevans@latest\n",[5211,19579,19580,19584,19593,19597,19601],{"__ignoreMap":5461},[5465,19581,19582],{"class":5467,"line":5468},[5465,19583,17184],{"class":5471},[5465,19585,19586,19588,19590],{"class":5467,"line":5475},[5465,19587,7663],{"class":5538},[5465,19589,7666],{"class":5503},[5465,19591,19592],{"class":5503}," evans\n",[5465,19594,19595],{"class":5467,"line":5493},[5465,19596,5529],{"emptyLinePlaceholder":5528},[5465,19598,19599],{"class":5467,"line":5507},[5465,19600,17202],{"class":5471},[5465,19602,19603,19605,19607],{"class":5467,"line":5519},[5465,19604,5460],{"class":5538},[5465,19606,7666],{"class":5503},[5465,19608,19609],{"class":5503}," github.com\u002Fktr0731\u002Fevans@latest\n",[5280,19611,19612],{},[5232,19613,19614],{},"Використання:",[5456,19616,19618],{"className":7649,"code":19617,"language":7651,"meta":5461,"style":5461},"# Інтерактивний режим\nevans --host localhost --port 50051 -r repl\n\n# CLI режим\nevans --host localhost --port 50051 cli call userservice.UserService.CreateUser\n",[5211,19619,19620,19625,19648,19652,19657],{"__ignoreMap":5461},[5465,19621,19622],{"class":5467,"line":5468},[5465,19623,19624],{"class":5471},"# Інтерактивний режим\n",[5465,19626,19627,19630,19633,19636,19639,19642,19645],{"class":5467,"line":5475},[5465,19628,19629],{"class":5538},"evans",[5465,19631,19632],{"class":5478}," --host",[5465,19634,19635],{"class":5503}," localhost",[5465,19637,19638],{"class":5478}," --port",[5465,19640,19641],{"class":7522}," 50051",[5465,19643,19644],{"class":5478}," -r",[5465,19646,19647],{"class":5503}," repl\n",[5465,19649,19650],{"class":5467,"line":5493},[5465,19651,5529],{"emptyLinePlaceholder":5528},[5465,19653,19654],{"class":5467,"line":5507},[5465,19655,19656],{"class":5471},"# CLI режим\n",[5465,19658,19659,19661,19663,19665,19667,19669,19672,19675],{"class":5467,"line":5519},[5465,19660,19629],{"class":5538},[5465,19662,19632],{"class":5478},[5465,19664,19635],{"class":5503},[5465,19666,19638],{"class":5478},[5465,19668,19641],{"class":7522},[5465,19670,19671],{"class":5503}," cli",[5465,19673,19674],{"class":5503}," call",[5465,19676,19134],{"class":5503},[5280,19678,19679,6758,19681],{},[5232,19680,7435],{},[7379,19682,19546],{"href":19683,"rel":19684},"https:\u002F\u002Fgithub.com\u002Fktr0731\u002Fevans",[7383],[5267,19686],{},[5270,19688,19690],{"id":19689},"порівняння-rest-vs-grpc-коли-використовувати-кожен-підхід","Порівняння REST vs gRPC: коли використовувати кожен підхід",[5275,19692,19694],{"id":19693},"порівняльна-таблиця-характеристик","Порівняльна таблиця характеристик",[6855,19696,19697,19710],{},[6858,19698,19699],{},[6861,19700,19701,19704,19707],{},[6864,19702,19703],{},"Критерій",[6864,19705,19706],{},"REST (JSON\u002FHTTP)",[6864,19708,19709],{},"gRPC (Protobuf\u002FHTTP2)",[6877,19711,19712,19725,19738,19751,19764,19777,19793,19806,19819,19832,19845,19858,19871,19884],{},[6861,19713,19714,19719,19722],{},[6882,19715,19716],{},[5232,19717,19718],{},"Формат даних",[6882,19720,19721],{},"JSON (текстовий)",[6882,19723,19724],{},"Protobuf (бінарний)",[6861,19726,19727,19732,19735],{},[6882,19728,19729],{},[5232,19730,19731],{},"Транспортний протокол",[6882,19733,19734],{},"HTTP\u002F1.1 (зазвичай)",[6882,19736,19737],{},"HTTP\u002F2 (обов'язково)",[6861,19739,19740,19745,19748],{},[6882,19741,19742],{},[5232,19743,19744],{},"Продуктивність серіалізації",[6882,19746,19747],{},"Повільна (парсинг JSON)",[6882,19749,19750],{},"Дуже швидка (бінарний формат)",[6861,19752,19753,19758,19761],{},[6882,19754,19755],{},[5232,19756,19757],{},"Розмір повідомлення",[6882,19759,19760],{},"Великий (текст + ключі)",[6882,19762,19763],{},"Малий (30–50% менше JSON)",[6861,19765,19766,19771,19774],{},[6882,19767,19768],{},[5232,19769,19770],{},"Підтримка потокових операцій",[6882,19772,19773],{},"Обмежена (SSE, WebSocket окремо)",[6882,19775,19776],{},"Нативна (4 типи streaming)",[6861,19778,19779,19784,19787],{},[6882,19780,19781],{},[5232,19782,19783],{},"Контракти API",[6882,19785,19786],{},"Описові (OpenAPI\u002FSwagger)",[6882,19788,19789,19790,19792],{},"Обов'язкові (",[5211,19791,5213],{}," файли)",[6861,19794,19795,19800,19803],{},[6882,19796,19797],{},[5232,19798,19799],{},"Типобезпека",[6882,19801,19802],{},"Відсутня (runtime перевірки)",[6882,19804,19805],{},"Повна (compile-time)",[6861,19807,19808,19813,19816],{},[6882,19809,19810],{},[5232,19811,19812],{},"Кодогенерація клієнтів",[6882,19814,19815],{},"Опціональна, часто неповна",[6882,19817,19818],{},"Автоматична для 10+ мов",[6861,19820,19821,19826,19829],{},[6882,19822,19823],{},[5232,19824,19825],{},"Читабельність для людини",[6882,19827,19828],{},"Висока (текстовий JSON)",[6882,19830,19831],{},"Низька (бінарний формат)",[6861,19833,19834,19839,19842],{},[6882,19835,19836],{},[5232,19837,19838],{},"Підтримка браузерами",[6882,19840,19841],{},"Нативна (Fetch API)",[6882,19843,19844],{},"Потребує grpc-web proxy",[6861,19846,19847,19852,19855],{},[6882,19848,19849],{},[5232,19850,19851],{},"Проходження через проксі\u002FCDN",[6882,19853,19854],{},"Ідеальне",[6882,19856,19857],{},"Може вимагати налаштувань",[6861,19859,19860,19865,19868],{},[6882,19861,19862],{},[5232,19863,19864],{},"Складність налагодження",[6882,19866,19867],{},"Проста (curl, Postman)",[6882,19869,19870],{},"Середня (grpcurl, BloomRPC)",[6861,19872,19873,19878,19881],{},[6882,19874,19875],{},[5232,19876,19877],{},"Екосистема інструментів",[6882,19879,19880],{},"Величезна",[6882,19882,19883],{},"Швидко зростає",[6861,19885,19886,19891,19894],{},[6882,19887,19888],{},[5232,19889,19890],{},"Швидкість розробки",[6882,19892,19893],{},"Дуже швидка",[6882,19895,19896],{},"Середня (потрібна компіляція .proto)",[5275,19898,19900],{"id":19899},"рекомендації-щодо-вибору-технології","Рекомендації щодо вибору технології",[5280,19902,19903],{},[5232,19904,19905],{},"Використовуйте REST, якщо:",[6403,19907,19908,19914,19920,19926,19935],{},[5201,19909,19910,19913],{},[5232,19911,19912],{},"Публічне API для сторонніх інтеграторів."," REST є універсальним стандартом, що підтримується всіма мовами програмування та інструментами. Документація OpenAPI\u002FSwagger дозволяє розробникам легко зрозуміти API.",[5201,19915,19916,19919],{},[5232,19917,19918],{},"Клієнти — вебдодатки у браузері."," Fetch API та XMLHttpRequest нативно підтримують HTTP\u002FJSON. gRPC вимагає використання grpc-web із проксі-сервером (Envoy).",[5201,19921,19922,19925],{},[5232,19923,19924],{},"Простота налагодження та читабельність."," JSON-відповіді можна переглядати у браузері, логах, інструментах моніторингу без спеціальних декодерів.",[5201,19927,19928,19931,19932,19934],{},[5232,19929,19930],{},"Швидкість розробки критична."," REST дозволяє швидко створювати прототипи без налаштування компіляції ",[5211,19933,5213],{}," та кодогенерації.",[5201,19936,19937,19940],{},[5232,19938,19939],{},"Сумісність зі старою інфраструктурою."," Багато корпоративних проксі-серверів, брандмауерів та CDN оптимізовані для HTTP\u002F1.1 та можуть некоректно обробляти HTTP\u002F2 або блокувати бінарні протоколи.",[5280,19942,19943],{},[5232,19944,19945],{},"Використовуйте gRPC, якщо:",[6403,19947,19948,19954,19960,19966,19976],{},[5201,19949,19950,19953],{},[5232,19951,19952],{},"Міжсервісна комунікація у мікросервісній архітектурі."," gRPC забезпечує високу продуктивність, строгі контракти та автоматичну кодогенерацію для polyglot систем (Go ↔ Java ↔ Python ↔ C#).",[5201,19955,19956,19959],{},[5232,19957,19958],{},"Критична продуктивність та низька затримка."," Бінарна серіалізація Protobuf у 3–10 разів швидша за JSON. HTTP\u002F2 мультиплексування зменшує накладні витрати на встановлення з'єднань.",[5201,19961,19962,19965],{},[5232,19963,19964],{},"Потокова передача даних."," gRPC нативно підтримує Server Streaming, Client Streaming та Bidirectional Streaming без необхідності використання окремих протоколів (SSE, WebSocket).",[5201,19967,19968,19971,19972,19975],{},[5232,19969,19970],{},"Строга типізація та версіювання."," Protocol Buffers забезпечують прямую та зворотну сумісність (",[5284,19973,19974],{},"forward\u002Fbackward compatibility",") через field numbers. Зміни схеми автоматично перевіряються на етапі компіляції.",[5201,19977,19978,19981],{},[5232,19979,19980],{},"Полімовна кодогенерація."," Якщо у вас є клієнти на різних мовах (мобільний додаток на Kotlin, десктоп на C#, бекенд на Go), gRPC автоматично генерує типобезпечні клієнтські бібліотеки для всіх платформ.",[5430,19983,19984,19985,19988],{},"У багатьох сучасних системах використовується ",[5232,19986,19987],{},"гібридний підхід",": REST API для зовнішніх клієнтів (вебдодатки, мобільні застосунки, сторонні інтегратори) та gRPC для внутрішньої комунікації між мікросервісами. Наприклад, API Gateway (наприклад, на базі Kong або Envoy) приймає HTTP\u002FJSON запити від клієнтів та перетворює їх у gRPC виклики до бекенд-сервісів.",[5267,19990],{},[5270,19992,19994],{"id":19993},"розширені-теми-grpc","Розширені теми gRPC",[5275,19996,19998],{"id":19997},"балансування-навантаження-та-service-discovery","Балансування навантаження та Service Discovery",[5280,20000,20001,20002,6446],{},"У продакшн-середовищі gRPC-сервіси зазвичай розгортаються у кількох екземплярах для забезпечення високої доступності та масштабованості. Клієнти повинні розподіляти запити між екземплярами через балансування навантаження (",[5284,20003,20004],{},"load balancing",[5280,20006,20007],{},[5232,20008,20009],{},"Два підходи до балансування навантаження у gRPC:",[5280,20011,20012],{},[5232,20013,20014],{},"1. Проксі-балансування (Proxy Load Balancing).",[5280,20016,20017,20018,20021],{},"Клієнт підключається до централізованого балансувальника навантаження (Nginx, HAProxy, Envoy), який розподіляє з'єднання між бекенд-серверами. Це найпростіший підхід, але додає додатковий мережевий перехід (",[5284,20019,20020],{},"network hop",") та точку відмови.",[5456,20023,20026],{"className":20024,"code":20025,"language":7862},[7860],"Client → [Load Balancer] → {gRPC Server 1, gRPC Server 2, gRPC Server 3}\n",[5211,20027,20025],{"__ignoreMap":5461},[5280,20029,20030],{},[5232,20031,20032],{},"2. Клієнтське балансування (Client-side Load Balancing).",[5280,20034,20035],{},"gRPC-клієнт безпосередньо знає про всі доступні сервери та самостійно розподіляє запити між ними. Це вимагає інтеграції з системою Service Discovery (Consul, etcd, Kubernetes DNS).",[5456,20037,20039],{"className":5458,"code":20038,"language":5460,"meta":5461,"style":5461},"import (\n    \"google.golang.org\u002Fgrpc\"\n    \"google.golang.org\u002Fgrpc\u002Fbalancer\u002Froundrobin\"\n    \"google.golang.org\u002Fgrpc\u002Fresolver\"\n)\n\nfunc main() {\n    \u002F\u002F Підключення із клієнтським балансуванням round-robin\n    conn, err := grpc.Dial(\n        \"dns:\u002F\u002F\u002Fuserservice.default.svc.cluster.local:50051\",\n        grpc.WithDefaultServiceConfig(`{\"loadBalancingPolicy\":\"round_robin\"}`),\n        grpc.WithTransportCredentials(insecure.NewCredentials()),\n    )\n    \n    \u002F\u002F gRPC автоматично виявляє всі IP-адреси за DNS-записом\n    \u002F\u002F та розподіляє запити між ними\n}\n",[5211,20040,20041,20047,20051,20056,20061,20065,20069,20077,20082,20100,20107,20123,20141,20145,20149,20154,20159],{"__ignoreMap":5461},[5465,20042,20043,20045],{"class":5467,"line":5468},[5465,20044,9812],{"class":5675},[5465,20046,7535],{"class":5489},[5465,20048,20049],{"class":5467,"line":5475},[5465,20050,11246],{"class":5503},[5465,20052,20053],{"class":5467,"line":5493},[5465,20054,20055],{"class":5503},"    \"google.golang.org\u002Fgrpc\u002Fbalancer\u002Froundrobin\"\n",[5465,20057,20058],{"class":5467,"line":5507},[5465,20059,20060],{"class":5503},"    \"google.golang.org\u002Fgrpc\u002Fresolver\"\n",[5465,20062,20063],{"class":5467,"line":5519},[5465,20064,5591],{"class":5489},[5465,20066,20067],{"class":5467,"line":5525},[5465,20068,5529],{"emptyLinePlaceholder":5528},[5465,20070,20071,20073,20075],{"class":5467,"line":5532},[5465,20072,5535],{"class":5478},[5465,20074,11266],{"class":5538},[5465,20076,11269],{"class":5489},[5465,20078,20079],{"class":5467,"line":5565},[5465,20080,20081],{"class":5471},"    \u002F\u002F Підключення із клієнтським балансуванням round-robin\n",[5465,20083,20084,20086,20088,20090,20092,20094,20096,20098],{"class":5467,"line":5594},[5465,20085,11743],{"class":5496},[5465,20087,5336],{"class":5489},[5465,20089,5608],{"class":5496},[5465,20091,5576],{"class":5489},[5465,20093,7555],{"class":5496},[5465,20095,5214],{"class":5489},[5465,20097,11756],{"class":5538},[5465,20099,5621],{"class":5489},[5465,20101,20102,20105],{"class":5467,"line":5600},[5465,20103,20104],{"class":5503},"        \"dns:\u002F\u002F\u002Fuserservice.default.svc.cluster.local:50051\"",[5465,20106,5636],{"class":5489},[5465,20108,20109,20111,20113,20116,20118,20121],{"class":5467,"line":5624},[5465,20110,11375],{"class":5496},[5465,20112,5214],{"class":5489},[5465,20114,20115],{"class":5538},"WithDefaultServiceConfig",[5465,20117,5542],{"class":5489},[5465,20119,20120],{"class":5503},"`{\"loadBalancingPolicy\":\"round_robin\"}`",[5465,20122,5663],{"class":5489},[5465,20124,20125,20127,20129,20131,20133,20135,20137,20139],{"class":5467,"line":5639},[5465,20126,11375],{"class":5496},[5465,20128,5214],{"class":5489},[5465,20130,11774],{"class":5538},[5465,20132,5542],{"class":5489},[5465,20134,11779],{"class":5496},[5465,20136,5214],{"class":5489},[5465,20138,11784],{"class":5538},[5465,20140,15298],{"class":5489},[5465,20142,20143],{"class":5467,"line":5647},[5465,20144,5669],{"class":5489},[5465,20146,20147],{"class":5467,"line":5666},[5465,20148,5597],{"class":5489},[5465,20150,20151],{"class":5467,"line":5672},[5465,20152,20153],{"class":5471},"    \u002F\u002F gRPC автоматично виявляє всі IP-адреси за DNS-записом\n",[5465,20155,20156],{"class":5467,"line":5690},[5465,20157,20158],{"class":5471},"    \u002F\u002F та розподіляє запити між ними\n",[5465,20160,20161],{"class":5467,"line":5699},[5465,20162,5522],{"class":5489},[6839,20164,20165,20166,20169],{},"У Kubernetes використовуйте ",[5232,20167,20168],{},"headless services"," (без ClusterIP) для клієнтського балансування gRPC. Це дозволяє DNS повертати список IP-адрес всіх подів сервісу, а gRPC-клієнт автоматично розподіляє навантаження між ними.",[5275,20171,20173],{"id":20172},"deadline-propagation-та-таймаути","Deadline propagation та таймаути",[5280,20175,20176,20177,5321,20180,20183],{},"У розподілених системах критично важливо встановлювати таймаути для кожного запиту, щоб запобігти витоку ресурсів у випадку зависання downstream-сервісів. gRPC підтримує ",[5232,20178,20179],{},"розповсюдження deadline",[5284,20181,20182],{},"deadline propagation",") — автоматичну передачу залишкового часу запиту через ланцюжок викликів.",[5456,20185,20187],{"className":5458,"code":20186,"language":5460,"meta":5461,"style":5461},"func main() {\n    conn, _ := grpc.Dial(\"localhost:50051\", grpc.WithInsecure())\n    client := pb.NewUserServiceClient(conn)\n    \n    \u002F\u002F Встановлюємо deadline 5 секунд від поточного моменту\n    ctx, cancel := context.WithDeadline(\n        context.Background(),\n        time.Now().Add(5*time.Second),\n    )\n    defer cancel()\n    \n    \u002F\u002F Якщо UserService викликає інший gRPC-сервіс (OrderService),\n    \u002F\u002F він автоматично передає залишковий час (наприклад, 4.5 с)\n    resp, err := client.CreateUser(ctx, &pb.CreateUserRequest{...})\n    \n    if err != nil {\n        st, _ := status.FromError(err)\n        if st.Code() == codes.DeadlineExceeded {\n            log.Println(\"Request timed out\")\n        }\n    }\n}\n",[5211,20188,20189,20197,20229,20247,20251,20256,20275,20286,20313,20317,20325,20329,20334,20339,20370,20374,20386,20408,20428,20443,20447,20451],{"__ignoreMap":5461},[5465,20190,20191,20193,20195],{"class":5467,"line":5468},[5465,20192,5535],{"class":5478},[5465,20194,11266],{"class":5538},[5465,20196,11269],{"class":5489},[5465,20198,20199,20201,20203,20205,20207,20209,20211,20213,20215,20218,20220,20222,20224,20227],{"class":5467,"line":5475},[5465,20200,11743],{"class":5496},[5465,20202,5336],{"class":5489},[5465,20204,5573],{"class":5496},[5465,20206,5576],{"class":5489},[5465,20208,7555],{"class":5496},[5465,20210,5214],{"class":5489},[5465,20212,11756],{"class":5538},[5465,20214,5542],{"class":5489},[5465,20216,20217],{"class":5503},"\"localhost:50051\"",[5465,20219,5336],{"class":5489},[5465,20221,7555],{"class":5496},[5465,20223,5214],{"class":5489},[5465,20225,20226],{"class":5538},"WithInsecure",[5465,20228,12206],{"class":5489},[5465,20230,20231,20233,20235,20237,20239,20241,20243,20245],{"class":5467,"line":5493},[5465,20232,11875],{"class":5496},[5465,20234,5576],{"class":5489},[5465,20236,9928],{"class":5496},[5465,20238,5214],{"class":5489},[5465,20240,11884],{"class":5538},[5465,20242,5542],{"class":5489},[5465,20244,11889],{"class":5496},[5465,20246,5591],{"class":5489},[5465,20248,20249],{"class":5467,"line":5507},[5465,20250,5597],{"class":5489},[5465,20252,20253],{"class":5467,"line":5519},[5465,20254,20255],{"class":5471},"    \u002F\u002F Встановлюємо deadline 5 секунд від поточного моменту\n",[5465,20257,20258,20260,20262,20264,20266,20268,20270,20273],{"class":5467,"line":5525},[5465,20259,10104],{"class":5496},[5465,20261,5336],{"class":5489},[5465,20263,11964],{"class":5496},[5465,20265,5576],{"class":5489},[5465,20267,11969],{"class":5496},[5465,20269,5214],{"class":5489},[5465,20271,20272],{"class":5538},"WithDeadline",[5465,20274,5621],{"class":5489},[5465,20276,20277,20280,20282,20284],{"class":5467,"line":5532},[5465,20278,20279],{"class":5496},"        context",[5465,20281,5214],{"class":5489},[5465,20283,11983],{"class":5538},[5465,20285,10436],{"class":5489},[5465,20287,20288,20291,20293,20295,20297,20299,20301,20303,20305,20307,20309,20311],{"class":5467,"line":5565},[5465,20289,20290],{"class":5496},"        time",[5465,20292,5214],{"class":5489},[5465,20294,13862],{"class":5538},[5465,20296,10856],{"class":5489},[5465,20298,15257],{"class":5538},[5465,20300,5542],{"class":5489},[5465,20302,11989],{"class":7522},[5465,20304,10091],{"class":5489},[5465,20306,11994],{"class":5496},[5465,20308,5214],{"class":5489},[5465,20310,11999],{"class":5496},[5465,20312,5663],{"class":5489},[5465,20314,20315],{"class":5467,"line":5594},[5465,20316,5669],{"class":5489},[5465,20318,20319,20321,20323],{"class":5467,"line":5600},[5465,20320,5708],{"class":5675},[5465,20322,12008],{"class":5538},[5465,20324,5724],{"class":5489},[5465,20326,20327],{"class":5467,"line":5624},[5465,20328,5597],{"class":5489},[5465,20330,20331],{"class":5467,"line":5639},[5465,20332,20333],{"class":5471},"    \u002F\u002F Якщо UserService викликає інший gRPC-сервіс (OrderService),\n",[5465,20335,20336],{"class":5467,"line":5647},[5465,20337,20338],{"class":5471},"    \u002F\u002F він автоматично передає залишковий час (наприклад, 4.5 с)\n",[5465,20340,20341,20343,20345,20347,20349,20351,20353,20355,20357,20359,20361,20363,20365,20367],{"class":5467,"line":5666},[5465,20342,5603],{"class":5496},[5465,20344,5336],{"class":5489},[5465,20346,5608],{"class":5496},[5465,20348,5576],{"class":5489},[5465,20350,5878],{"class":5496},[5465,20352,5214],{"class":5489},[5465,20354,5912],{"class":5538},[5465,20356,5542],{"class":5489},[5465,20358,5917],{"class":5496},[5465,20360,5920],{"class":5489},[5465,20362,9928],{"class":5482},[5465,20364,5214],{"class":5489},[5465,20366,9007],{"class":5482},[5465,20368,20369],{"class":5489},"{...})\n",[5465,20371,20372],{"class":5467,"line":5672},[5465,20373,5597],{"class":5489},[5465,20375,20376,20378,20380,20382,20384],{"class":5467,"line":5690},[5465,20377,5676],{"class":5675},[5465,20379,5679],{"class":5496},[5465,20381,5682],{"class":5489},[5465,20383,5685],{"class":5478},[5465,20385,5490],{"class":5489},[5465,20387,20388,20390,20392,20394,20396,20398,20400,20402,20404,20406],{"class":5467,"line":5699},[5465,20389,13292],{"class":5496},[5465,20391,5336],{"class":5489},[5465,20393,5573],{"class":5496},[5465,20395,5576],{"class":5489},[5465,20397,10184],{"class":5496},[5465,20399,5214],{"class":5489},[5465,20401,13306],{"class":5538},[5465,20403,5542],{"class":5489},[5465,20405,5608],{"class":5496},[5465,20407,5591],{"class":5489},[5465,20409,20410,20412,20414,20416,20418,20420,20422,20424,20426],{"class":5467,"line":5705},[5465,20411,10843],{"class":5675},[5465,20413,13372],{"class":5496},[5465,20415,5214],{"class":5489},[5465,20417,13377],{"class":5538},[5465,20419,10168],{"class":5489},[5465,20421,10194],{"class":5496},[5465,20423,5214],{"class":5489},[5465,20425,10317],{"class":5496},[5465,20427,5490],{"class":5489},[5465,20429,20430,20432,20434,20436,20438,20441],{"class":5467,"line":5727},[5465,20431,10875],{"class":5496},[5465,20433,5214],{"class":5489},[5465,20435,11098],{"class":5538},[5465,20437,5542],{"class":5489},[5465,20439,20440],{"class":5503},"\"Request timed out\"",[5465,20442,5591],{"class":5489},[5465,20444,20445],{"class":5467,"line":5732},[5465,20446,10928],{"class":5489},[5465,20448,20449],{"class":5467,"line":5755},[5465,20450,5702],{"class":5489},[5465,20452,20453],{"class":5467,"line":5786},[5465,20454,5522],{"class":5489},[5280,20456,20457],{},[5232,20458,20459],{},"Best practices для таймаутів:",[5198,20461,20462,20465,20476],{},[5201,20463,20464],{},"Встановлюйте розумні таймаути на основі SLA системи (зазвичай 1–10 секунд для синхронних операцій).",[5201,20466,20467,20468,20471,20472,20475],{},"Використовуйте ",[5211,20469,20470],{},"context.WithTimeout()"," замість ",[5211,20473,20474],{},"context.WithDeadline()"," для відносних таймаутів.",[5201,20477,20478,20479,20481],{},"Логуйте всі помилки ",[5211,20480,10317],{}," та аналізуйте їх для оптимізації продуктивності.",[5275,20483,20485],{"id":20484},"mutual-tls-mtls-для-міжсервісної-автентифікації","Mutual TLS (mTLS) для міжсервісної автентифікації",[5280,20487,20488,20489,20492,20493,20496],{},"У нульових довіри (",[5284,20490,20491],{},"zero trust",") архітектурах кожен сервіс повинен автентифікувати себе перед іншими сервісами через взаємну TLS (",[5284,20494,20495],{},"mutual TLS, mTLS","). Це означає, що як клієнт, так і сервер надають сертифікати для перевірки ідентичності.",[5280,20498,20499],{},[5232,20500,20501],{},"Налаштування mTLS на gRPC-сервері:",[5456,20503,20505],{"className":5458,"code":20504,"language":5460,"meta":5461,"style":5461},"import (\n    \"crypto\u002Ftls\"\n    \"crypto\u002Fx509\"\n    \"os\"\n    \n    \"google.golang.org\u002Fgrpc\"\n    \"google.golang.org\u002Fgrpc\u002Fcredentials\"\n)\n\nfunc main() {\n    \u002F\u002F Завантажуємо сертифікат та ключ сервера\n    serverCert, err := tls.LoadX509KeyPair(\"certs\u002Fserver.crt\", \"certs\u002Fserver.key\")\n    if err != nil {\n        log.Fatalf(\"Failed to load server cert: %v\", err)\n    }\n    \n    \u002F\u002F Завантажуємо CA сертифікат для перевірки клієнтів\n    caCert, err := os.ReadFile(\"certs\u002Fca.crt\")\n    if err != nil {\n        log.Fatalf(\"Failed to load CA cert: %v\", err)\n    }\n    \n    certPool := x509.NewCertPool()\n    certPool.AppendCertsFromPEM(caCert)\n    \n    \u002F\u002F Налаштовуємо TLS із вимогою верифікації клієнтського сертифіката\n    tlsConfig := &tls.Config{\n        Certificates: []tls.Certificate{serverCert},\n        ClientAuth:   tls.RequireAndVerifyClientCert,  \u002F\u002F Вимагаємо mTLS\n        ClientCAs:    certPool,\n    }\n    \n    creds := credentials.NewTLS(tlsConfig)\n    \n    grpcServer := grpc.NewServer(grpc.Creds(creds))\n    \u002F\u002F ... реєстрація сервісів\n}\n",[5211,20506,20507,20513,20517,20522,20527,20531,20535,20539,20543,20547,20555,20560,20589,20601,20624,20628,20632,20637,20660,20672,20695,20699,20703,20717,20731,20735,20740,20754,20772,20790,20801,20805,20809,20827,20831,20857,20861],{"__ignoreMap":5461},[5465,20508,20509,20511],{"class":5467,"line":5468},[5465,20510,9812],{"class":5675},[5465,20512,7535],{"class":5489},[5465,20514,20515],{"class":5467,"line":5475},[5465,20516,15959],{"class":5503},[5465,20518,20519],{"class":5467,"line":5493},[5465,20520,20521],{"class":5503},"    \"crypto\u002Fx509\"\n",[5465,20523,20524],{"class":5467,"line":5507},[5465,20525,20526],{"class":5503},"    \"os\"\n",[5465,20528,20529],{"class":5467,"line":5519},[5465,20530,5597],{"class":5489},[5465,20532,20533],{"class":5467,"line":5525},[5465,20534,11246],{"class":5503},[5465,20536,20537],{"class":5467,"line":5532},[5465,20538,15968],{"class":5503},[5465,20540,20541],{"class":5467,"line":5565},[5465,20542,5591],{"class":5489},[5465,20544,20545],{"class":5467,"line":5594},[5465,20546,5529],{"emptyLinePlaceholder":5528},[5465,20548,20549,20551,20553],{"class":5467,"line":5600},[5465,20550,5535],{"class":5478},[5465,20552,11266],{"class":5538},[5465,20554,11269],{"class":5489},[5465,20556,20557],{"class":5467,"line":5624},[5465,20558,20559],{"class":5471},"    \u002F\u002F Завантажуємо сертифікат та ключ сервера\n",[5465,20561,20562,20565,20567,20569,20571,20573,20575,20577,20579,20582,20584,20587],{"class":5467,"line":5639},[5465,20563,20564],{"class":5496},"    serverCert",[5465,20566,5336],{"class":5489},[5465,20568,5608],{"class":5496},[5465,20570,5576],{"class":5489},[5465,20572,16003],{"class":5496},[5465,20574,5214],{"class":5489},[5465,20576,16008],{"class":5538},[5465,20578,5542],{"class":5489},[5465,20580,20581],{"class":5503},"\"certs\u002Fserver.crt\"",[5465,20583,5336],{"class":5489},[5465,20585,20586],{"class":5503},"\"certs\u002Fserver.key\"",[5465,20588,5591],{"class":5489},[5465,20590,20591,20593,20595,20597,20599],{"class":5467,"line":5647},[5465,20592,5676],{"class":5675},[5465,20594,5679],{"class":5496},[5465,20596,5682],{"class":5489},[5465,20598,5685],{"class":5478},[5465,20600,5490],{"class":5489},[5465,20602,20603,20605,20607,20609,20611,20614,20616,20618,20620,20622],{"class":5467,"line":5666},[5465,20604,11046],{"class":5496},[5465,20606,5214],{"class":5489},[5465,20608,11326],{"class":5538},[5465,20610,5542],{"class":5489},[5465,20612,20613],{"class":5503},"\"Failed to load server cert: ",[5465,20615,10887],{"class":5496},[5465,20617,5813],{"class":5503},[5465,20619,5336],{"class":5489},[5465,20621,5608],{"class":5496},[5465,20623,5591],{"class":5489},[5465,20625,20626],{"class":5467,"line":5672},[5465,20627,5702],{"class":5489},[5465,20629,20630],{"class":5467,"line":5690},[5465,20631,5597],{"class":5489},[5465,20633,20634],{"class":5467,"line":5699},[5465,20635,20636],{"class":5471},"    \u002F\u002F Завантажуємо CA сертифікат для перевірки клієнтів\n",[5465,20638,20639,20641,20643,20645,20647,20649,20651,20653,20655,20658],{"class":5467,"line":5705},[5465,20640,16209],{"class":5496},[5465,20642,5336],{"class":5489},[5465,20644,5608],{"class":5496},[5465,20646,5576],{"class":5489},[5465,20648,16218],{"class":5496},[5465,20650,5214],{"class":5489},[5465,20652,16223],{"class":5538},[5465,20654,5542],{"class":5489},[5465,20656,20657],{"class":5503},"\"certs\u002Fca.crt\"",[5465,20659,5591],{"class":5489},[5465,20661,20662,20664,20666,20668,20670],{"class":5467,"line":5727},[5465,20663,5676],{"class":5675},[5465,20665,5679],{"class":5496},[5465,20667,5682],{"class":5489},[5465,20669,5685],{"class":5478},[5465,20671,5490],{"class":5489},[5465,20673,20674,20676,20678,20680,20682,20685,20687,20689,20691,20693],{"class":5467,"line":5732},[5465,20675,11046],{"class":5496},[5465,20677,5214],{"class":5489},[5465,20679,11326],{"class":5538},[5465,20681,5542],{"class":5489},[5465,20683,20684],{"class":5503},"\"Failed to load CA cert: ",[5465,20686,10887],{"class":5496},[5465,20688,5813],{"class":5503},[5465,20690,5336],{"class":5489},[5465,20692,5608],{"class":5496},[5465,20694,5591],{"class":5489},[5465,20696,20697],{"class":5467,"line":5755},[5465,20698,5702],{"class":5489},[5465,20700,20701],{"class":5467,"line":5786},[5465,20702,5597],{"class":5489},[5465,20704,20705,20707,20709,20711,20713,20715],{"class":5467,"line":5831},[5465,20706,16269],{"class":5496},[5465,20708,5576],{"class":5489},[5465,20710,16274],{"class":5496},[5465,20712,5214],{"class":5489},[5465,20714,16279],{"class":5538},[5465,20716,5724],{"class":5489},[5465,20718,20719,20721,20723,20725,20727,20729],{"class":5467,"line":5836},[5465,20720,16269],{"class":5496},[5465,20722,5214],{"class":5489},[5465,20724,16290],{"class":5538},[5465,20726,5542],{"class":5489},[5465,20728,16295],{"class":5496},[5465,20730,5591],{"class":5489},[5465,20732,20733],{"class":5467,"line":5841},[5465,20734,5597],{"class":5489},[5465,20736,20737],{"class":5467,"line":5850},[5465,20738,20739],{"class":5471},"    \u002F\u002F Налаштовуємо TLS із вимогою верифікації клієнтського сертифіката\n",[5465,20741,20742,20744,20746,20748,20750,20752],{"class":5467,"line":6213},[5465,20743,16060],{"class":5496},[5465,20745,10392],{"class":5489},[5465,20747,16003],{"class":5482},[5465,20749,5214],{"class":5489},[5465,20751,16069],{"class":5482},[5465,20753,5926],{"class":5489},[5465,20755,20756,20758,20760,20762,20764,20766,20768,20770],{"class":5467,"line":6218},[5465,20757,16076],{"class":5496},[5465,20759,16079],{"class":5489},[5465,20761,16003],{"class":5482},[5465,20763,5214],{"class":5489},[5465,20765,16086],{"class":5482},[5465,20767,10592],{"class":5489},[5465,20769,16464],{"class":5496},[5465,20771,16094],{"class":5489},[5465,20773,20774,20777,20779,20781,20783,20785,20787],{"class":5467,"line":6224},[5465,20775,20776],{"class":5496},"        ClientAuth",[5465,20778,12362],{"class":5489},[5465,20780,16003],{"class":5496},[5465,20782,5214],{"class":5489},[5465,20784,16480],{"class":5496},[5465,20786,14403],{"class":5489},[5465,20788,20789],{"class":5471},"\u002F\u002F Вимагаємо mTLS\n",[5465,20791,20792,20795,20797,20799],{"class":5467,"line":6230},[5465,20793,20794],{"class":5496},"        ClientCAs",[5465,20796,10460],{"class":5489},[5465,20798,16325],{"class":5496},[5465,20800,5636],{"class":5489},[5465,20802,20803],{"class":5467,"line":6236},[5465,20804,5702],{"class":5489},[5465,20806,20807],{"class":5467,"line":6242},[5465,20808,5597],{"class":5489},[5465,20810,20811,20813,20815,20817,20819,20821,20823,20825],{"class":5467,"line":6248},[5465,20812,16126],{"class":5496},[5465,20814,5576],{"class":5489},[5465,20816,16131],{"class":5496},[5465,20818,5214],{"class":5489},[5465,20820,16136],{"class":5538},[5465,20822,5542],{"class":5489},[5465,20824,16141],{"class":5496},[5465,20826,5591],{"class":5489},[5465,20828,20829],{"class":5467,"line":6254},[5465,20830,5597],{"class":5489},[5465,20832,20833,20835,20837,20839,20841,20843,20845,20847,20849,20851,20853,20855],{"class":5467,"line":6260},[5465,20834,11359],{"class":5496},[5465,20836,5576],{"class":5489},[5465,20838,7555],{"class":5496},[5465,20840,5214],{"class":5489},[5465,20842,11368],{"class":5538},[5465,20844,5542],{"class":5489},[5465,20846,7555],{"class":5496},[5465,20848,5214],{"class":5489},[5465,20850,16165],{"class":5538},[5465,20852,5542],{"class":5489},[5465,20854,16170],{"class":5496},[5465,20856,10799],{"class":5489},[5465,20858,20859],{"class":5467,"line":6265},[5465,20860,16177],{"class":5471},[5465,20862,20863],{"class":5467,"line":6270},[5465,20864,5522],{"class":5489},[5280,20866,20867],{},[5232,20868,20869],{},"Налаштування mTLS на gRPC-клієнті:",[5456,20871,20873],{"className":5458,"code":20872,"language":5460,"meta":5461,"style":5461},"func main() {\n    \u002F\u002F Завантажуємо клієнтський сертифікат та ключ\n    clientCert, err := tls.LoadX509KeyPair(\"certs\u002Fclient.crt\", \"certs\u002Fclient.key\")\n    if err != nil {\n        log.Fatalf(\"Failed to load client cert: %v\", err)\n    }\n    \n    \u002F\u002F Завантажуємо CA для перевірки серверного сертифіката\n    caCert, err := os.ReadFile(\"certs\u002Fca.crt\")\n    if err != nil {\n        log.Fatalf(\"Failed to load CA cert: %v\", err)\n    }\n    \n    certPool := x509.NewCertPool()\n    certPool.AppendCertsFromPEM(caCert)\n    \n    tlsConfig := &tls.Config{\n        Certificates: []tls.Certificate{clientCert},\n        RootCAs:      certPool,\n    }\n    \n    creds := credentials.NewTLS(tlsConfig)\n    \n    conn, err := grpc.Dial(\n        \"localhost:50051\",\n        grpc.WithTransportCredentials(creds),\n    )\n    \u002F\u002F ... використання клієнта\n}\n",[5211,20874,20875,20883,20888,20917,20929,20952,20956,20960,20965,20987,20999,21021,21025,21029,21043,21057,21061,21075,21094,21104,21108,21112,21130,21134,21152,21158,21172,21176,21180],{"__ignoreMap":5461},[5465,20876,20877,20879,20881],{"class":5467,"line":5468},[5465,20878,5535],{"class":5478},[5465,20880,11266],{"class":5538},[5465,20882,11269],{"class":5489},[5465,20884,20885],{"class":5467,"line":5475},[5465,20886,20887],{"class":5471},"    \u002F\u002F Завантажуємо клієнтський сертифікат та ключ\n",[5465,20889,20890,20893,20895,20897,20899,20901,20903,20905,20907,20910,20912,20915],{"class":5467,"line":5493},[5465,20891,20892],{"class":5496},"    clientCert",[5465,20894,5336],{"class":5489},[5465,20896,5608],{"class":5496},[5465,20898,5576],{"class":5489},[5465,20900,16003],{"class":5496},[5465,20902,5214],{"class":5489},[5465,20904,16008],{"class":5538},[5465,20906,5542],{"class":5489},[5465,20908,20909],{"class":5503},"\"certs\u002Fclient.crt\"",[5465,20911,5336],{"class":5489},[5465,20913,20914],{"class":5503},"\"certs\u002Fclient.key\"",[5465,20916,5591],{"class":5489},[5465,20918,20919,20921,20923,20925,20927],{"class":5467,"line":5507},[5465,20920,5676],{"class":5675},[5465,20922,5679],{"class":5496},[5465,20924,5682],{"class":5489},[5465,20926,5685],{"class":5478},[5465,20928,5490],{"class":5489},[5465,20930,20931,20933,20935,20937,20939,20942,20944,20946,20948,20950],{"class":5467,"line":5519},[5465,20932,11046],{"class":5496},[5465,20934,5214],{"class":5489},[5465,20936,11326],{"class":5538},[5465,20938,5542],{"class":5489},[5465,20940,20941],{"class":5503},"\"Failed to load client cert: ",[5465,20943,10887],{"class":5496},[5465,20945,5813],{"class":5503},[5465,20947,5336],{"class":5489},[5465,20949,5608],{"class":5496},[5465,20951,5591],{"class":5489},[5465,20953,20954],{"class":5467,"line":5525},[5465,20955,5702],{"class":5489},[5465,20957,20958],{"class":5467,"line":5532},[5465,20959,5597],{"class":5489},[5465,20961,20962],{"class":5467,"line":5565},[5465,20963,20964],{"class":5471},"    \u002F\u002F Завантажуємо CA для перевірки серверного сертифіката\n",[5465,20966,20967,20969,20971,20973,20975,20977,20979,20981,20983,20985],{"class":5467,"line":5594},[5465,20968,16209],{"class":5496},[5465,20970,5336],{"class":5489},[5465,20972,5608],{"class":5496},[5465,20974,5576],{"class":5489},[5465,20976,16218],{"class":5496},[5465,20978,5214],{"class":5489},[5465,20980,16223],{"class":5538},[5465,20982,5542],{"class":5489},[5465,20984,20657],{"class":5503},[5465,20986,5591],{"class":5489},[5465,20988,20989,20991,20993,20995,20997],{"class":5467,"line":5600},[5465,20990,5676],{"class":5675},[5465,20992,5679],{"class":5496},[5465,20994,5682],{"class":5489},[5465,20996,5685],{"class":5478},[5465,20998,5490],{"class":5489},[5465,21000,21001,21003,21005,21007,21009,21011,21013,21015,21017,21019],{"class":5467,"line":5624},[5465,21002,11046],{"class":5496},[5465,21004,5214],{"class":5489},[5465,21006,11326],{"class":5538},[5465,21008,5542],{"class":5489},[5465,21010,20684],{"class":5503},[5465,21012,10887],{"class":5496},[5465,21014,5813],{"class":5503},[5465,21016,5336],{"class":5489},[5465,21018,5608],{"class":5496},[5465,21020,5591],{"class":5489},[5465,21022,21023],{"class":5467,"line":5639},[5465,21024,5702],{"class":5489},[5465,21026,21027],{"class":5467,"line":5647},[5465,21028,5597],{"class":5489},[5465,21030,21031,21033,21035,21037,21039,21041],{"class":5467,"line":5666},[5465,21032,16269],{"class":5496},[5465,21034,5576],{"class":5489},[5465,21036,16274],{"class":5496},[5465,21038,5214],{"class":5489},[5465,21040,16279],{"class":5538},[5465,21042,5724],{"class":5489},[5465,21044,21045,21047,21049,21051,21053,21055],{"class":5467,"line":5672},[5465,21046,16269],{"class":5496},[5465,21048,5214],{"class":5489},[5465,21050,16290],{"class":5538},[5465,21052,5542],{"class":5489},[5465,21054,16295],{"class":5496},[5465,21056,5591],{"class":5489},[5465,21058,21059],{"class":5467,"line":5690},[5465,21060,5597],{"class":5489},[5465,21062,21063,21065,21067,21069,21071,21073],{"class":5467,"line":5699},[5465,21064,16060],{"class":5496},[5465,21066,10392],{"class":5489},[5465,21068,16003],{"class":5482},[5465,21070,5214],{"class":5489},[5465,21072,16069],{"class":5482},[5465,21074,5926],{"class":5489},[5465,21076,21077,21079,21081,21083,21085,21087,21089,21092],{"class":5467,"line":5705},[5465,21078,16076],{"class":5496},[5465,21080,16079],{"class":5489},[5465,21082,16003],{"class":5482},[5465,21084,5214],{"class":5489},[5465,21086,16086],{"class":5482},[5465,21088,10592],{"class":5489},[5465,21090,21091],{"class":5496},"clientCert",[5465,21093,16094],{"class":5489},[5465,21095,21096,21098,21100,21102],{"class":5467,"line":5727},[5465,21097,16320],{"class":5496},[5465,21099,10426],{"class":5489},[5465,21101,16325],{"class":5496},[5465,21103,5636],{"class":5489},[5465,21105,21106],{"class":5467,"line":5732},[5465,21107,5702],{"class":5489},[5465,21109,21110],{"class":5467,"line":5755},[5465,21111,5597],{"class":5489},[5465,21113,21114,21116,21118,21120,21122,21124,21126,21128],{"class":5467,"line":5786},[5465,21115,16126],{"class":5496},[5465,21117,5576],{"class":5489},[5465,21119,16131],{"class":5496},[5465,21121,5214],{"class":5489},[5465,21123,16136],{"class":5538},[5465,21125,5542],{"class":5489},[5465,21127,16141],{"class":5496},[5465,21129,5591],{"class":5489},[5465,21131,21132],{"class":5467,"line":5831},[5465,21133,5597],{"class":5489},[5465,21135,21136,21138,21140,21142,21144,21146,21148,21150],{"class":5467,"line":5836},[5465,21137,11743],{"class":5496},[5465,21139,5336],{"class":5489},[5465,21141,5608],{"class":5496},[5465,21143,5576],{"class":5489},[5465,21145,7555],{"class":5496},[5465,21147,5214],{"class":5489},[5465,21149,11756],{"class":5538},[5465,21151,5621],{"class":5489},[5465,21153,21154,21156],{"class":5467,"line":5841},[5465,21155,11763],{"class":5503},[5465,21157,5636],{"class":5489},[5465,21159,21160,21162,21164,21166,21168,21170],{"class":5467,"line":5850},[5465,21161,11375],{"class":5496},[5465,21163,5214],{"class":5489},[5465,21165,11774],{"class":5538},[5465,21167,5542],{"class":5489},[5465,21169,16170],{"class":5496},[5465,21171,5663],{"class":5489},[5465,21173,21174],{"class":5467,"line":6213},[5465,21175,5669],{"class":5489},[5465,21177,21178],{"class":5467,"line":6218},[5465,21179,16418],{"class":5471},[5465,21181,21182],{"class":5467,"line":6224},[5465,21183,5522],{"class":5489},[21185,21186,21187,21188,21191],"caution",{},"Управління сертифікатами у великих мікросервісних системах є складною задачею. Розгляньте використання ",[5232,21189,21190],{},"service mesh рішень"," (Istio, Linkerd, Consul Connect), що автоматизують виписування, ротацію та верифікацію сертифікатів через вбудовані Certificate Authorities.",[5275,21193,21195],{"id":21194},"service-mesh-автоматизація-мережевої-інфраструктури-для-мікросервісів","Service Mesh: автоматизація мережевої інфраструктури для мікросервісів",[5190,21197,21198],{},[5193,21199,21201,21207,21211,21215,21226,21231,21242,21247,21261,21266,21271,21294],{"icon":658,"title":21200},"🌐 Service Mesh екосистема",[5280,21202,21203,21206],{},[5232,21204,21205],{},"Service Mesh"," — це виділений інфраструктурний шар для керування міжсервісною комунікацією у мікросервісних архітектурах. Замість впровадження логіки мережевої комунікації у кожен сервіс, Service Mesh виносить її у окремі sidecar-проксі.",[5280,21208,21209],{},[5232,21210,15046],{},[5280,21212,21213],{},[5232,21214,16988],{},[5198,21216,21217,21220,21223],{},[5201,21218,21219],{},"Автоматичний mutual TLS (mTLS) між усіма сервісами",[5201,21221,21222],{},"Автоматична ротація сертифікатів",[5201,21224,21225],{},"Fine-grained access control (авторизація між сервісами)",[5280,21227,21228],{},[5232,21229,21230],{},"Спостережуваність (Observability):",[5198,21232,21233,21236,21239],{},[5201,21234,21235],{},"Автоматичний збір метрик (latency, throughput, error rate)",[5201,21237,21238],{},"Розподілений трейсинг (distributed tracing)",[5201,21240,21241],{},"Візуалізація топології сервісів",[5280,21243,21244],{},[5232,21245,21246],{},"Traffic Management:",[5198,21248,21249,21252,21255,21258],{},[5201,21250,21251],{},"Інтелектуальне балансування навантаження",[5201,21253,21254],{},"Canary deployments та blue-green deployments",[5201,21256,21257],{},"Circuit breaking та retries",[5201,21259,21260],{},"Rate limiting та quota management",[5280,21262,21263],{},[5232,21264,21265],{},"Популярні рішення:",[5280,21267,21268],{},[5232,21269,21270],{},"Istio",[5198,21272,21273,21282,21285,21288,21291],{},[5201,21274,21275,6758,21277],{},[5232,21276,19326],{},[7379,21278,21281],{"href":21279,"rel":21280},"https:\u002F\u002Fistio.io",[7383],"istio.io",[5201,21283,21284],{},"Найпопулярніший service mesh",[5201,21286,21287],{},"Використовує Envoy як sidecar proxy",[5201,21289,21290],{},"Інтеграція з Kubernetes, Prometheus, Jaeger",[5201,21292,21293],{},"Складніший у налаштуванні, але найбільш функціональний",[5193,21295,21298,21304,21313,21318,21338,21342,21356,21361,21768],{"icon":21296,"title":21297},"i-lucide-plug","🔌 Envoy Proxy",[5280,21299,21300,21303],{},[5232,21301,21302],{},"Envoy"," — це високопродуктивний edge та service proxy, написаний на C++",[5280,21305,21306,6758,21308],{},[5232,21307,7377],{},[7379,21309,21312],{"href":21310,"rel":21311},"https:\u002F\u002Fgithub.com\u002Fenvoyproxy\u002Fenvoy",[7383],"github.com\u002Fenvoyproxy\u002Fenvoy",[5280,21314,21315],{},[5232,21316,21317],{},"Ключові особливості:",[5198,21319,21320,21323,21326,21329,21332,21335],{},[5201,21321,21322],{},"Динамічна конфігурація через xDS API (без рестарту)",[5201,21324,21325],{},"HTTP\u002F1.1, HTTP\u002F2, HTTP\u002F3 (QUIC) підтримка",[5201,21327,21328],{},"gRPC transcoding (HTTP\u002FJSON → gRPC\u002FProtobuf)",[5201,21330,21331],{},"Advanced load balancing (ring hash, maglev, least request)",[5201,21333,21334],{},"Circuit breaking, outlier detection, rate limiting",[5201,21336,21337],{},"Observability: метрики, логи, distributed tracing",[5280,21339,21340],{},[5232,21341,19614],{},[5198,21343,21344,21347,21350,21353],{},[5201,21345,21346],{},"Як API Gateway перед gRPC сервісами",[5201,21348,21349],{},"Як sidecar proxy у Service Mesh (Istio, Consul Connect)",[5201,21351,21352],{},"Для gRPC-web — проксіювання gRPC до браузерів",[5201,21354,21355],{},"Load balancer для gRPC із service discovery",[5280,21357,21358],{},[5232,21359,21360],{},"Приклад конфігурації для gRPC routing:",[5456,21362,21364],{"className":19469,"code":21363,"language":19471,"meta":5461,"style":5461},"static_resources:\n  listeners:\n  - name: listener_0\n    address:\n      socket_address: { address: 0.0.0.0, port_value: 50051 }\n    filter_chains:\n    - filters:\n      - name: envoy.filters.network.http_connection_manager\n        typed_config:\n          \"@type\": type.googleapis.com\u002Fenvoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager\n          codec_type: AUTO\n          stat_prefix: ingress_http\n          route_config:\n            name: local_route\n            virtual_hosts:\n            - name: backend\n              domains: [\"*\"]\n              routes:\n              - match: { prefix: \"\u002F\" }\n                route:\n                  cluster: grpc_cluster\n                  timeout: 5s\n          http_filters:\n          - name: envoy.filters.http.router\n\n  clusters:\n  - name: grpc_cluster\n    type: STRICT_DNS\n    lb_policy: ROUND_ROBIN\n    typed_extension_protocol_options:\n      envoy.extensions.upstreams.http.v3.HttpProtocolOptions:\n        \"@type\": type.googleapis.com\u002Fenvoy.extensions.upstreams.http.v3.HttpProtocolOptions\n        explicit_http_config:\n          http2_protocol_options: {}\n    load_assignment:\n      cluster_name: grpc_cluster\n      endpoints:\n      - lb_endpoints:\n        - endpoint:\n            address:\n              socket_address: { address: userservice, port_value: 50051 }\n",[5211,21365,21366,21373,21380,21392,21399,21428,21435,21444,21456,21463,21473,21483,21493,21500,21510,21517,21529,21543,21550,21570,21577,21587,21597,21604,21616,21620,21627,21637,21647,21657,21664,21671,21681,21688,21696,21703,21712,21719,21728,21738,21745],{"__ignoreMap":5461},[5465,21367,21368,21371],{"class":5467,"line":5468},[5465,21369,21370],{"class":8155},"static_resources",[5465,21372,10332],{"class":5489},[5465,21374,21375,21378],{"class":5467,"line":5475},[5465,21376,21377],{"class":8155},"  listeners",[5465,21379,10332],{"class":5489},[5465,21381,21382,21385,21387,21389],{"class":5467,"line":5493},[5465,21383,21384],{"class":5489},"  - ",[5465,21386,6779],{"class":8155},[5465,21388,5807],{"class":5489},[5465,21390,21391],{"class":19483},"listener_0\n",[5465,21393,21394,21397],{"class":5467,"line":5507},[5465,21395,21396],{"class":8155},"    address",[5465,21398,10332],{"class":5489},[5465,21400,21401,21404,21407,21410,21412,21415,21417,21420,21422,21425],{"class":5467,"line":5519},[5465,21402,21403],{"class":8155},"      socket_address",[5465,21405,21406],{"class":5489},": { ",[5465,21408,21409],{"class":8155},"address",[5465,21411,5807],{"class":5489},[5465,21413,21414],{"class":7522},"0.0.0.0",[5465,21416,5336],{"class":5489},[5465,21418,21419],{"class":8155},"port_value",[5465,21421,5807],{"class":5489},[5465,21423,21424],{"class":7522},"50051",[5465,21426,21427],{"class":5489}," }\n",[5465,21429,21430,21433],{"class":5467,"line":5525},[5465,21431,21432],{"class":8155},"    filter_chains",[5465,21434,10332],{"class":5489},[5465,21436,21437,21439,21442],{"class":5467,"line":5532},[5465,21438,19503],{"class":5489},[5465,21440,21441],{"class":8155},"filters",[5465,21443,10332],{"class":5489},[5465,21445,21446,21449,21451,21453],{"class":5467,"line":5565},[5465,21447,21448],{"class":5489},"      - ",[5465,21450,6779],{"class":8155},[5465,21452,5807],{"class":5489},[5465,21454,21455],{"class":19483},"envoy.filters.network.http_connection_manager\n",[5465,21457,21458,21461],{"class":5467,"line":5594},[5465,21459,21460],{"class":8155},"        typed_config",[5465,21462,10332],{"class":5489},[5465,21464,21465,21468,21470],{"class":5467,"line":5600},[5465,21466,21467],{"class":5503},"          \"@type\"",[5465,21469,5807],{"class":5489},[5465,21471,21472],{"class":19483},"type.googleapis.com\u002Fenvoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager\n",[5465,21474,21475,21478,21480],{"class":5467,"line":5624},[5465,21476,21477],{"class":8155},"          codec_type",[5465,21479,5807],{"class":5489},[5465,21481,21482],{"class":19483},"AUTO\n",[5465,21484,21485,21488,21490],{"class":5467,"line":5639},[5465,21486,21487],{"class":8155},"          stat_prefix",[5465,21489,5807],{"class":5489},[5465,21491,21492],{"class":19483},"ingress_http\n",[5465,21494,21495,21498],{"class":5467,"line":5647},[5465,21496,21497],{"class":8155},"          route_config",[5465,21499,10332],{"class":5489},[5465,21501,21502,21505,21507],{"class":5467,"line":5666},[5465,21503,21504],{"class":8155},"            name",[5465,21506,5807],{"class":5489},[5465,21508,21509],{"class":19483},"local_route\n",[5465,21511,21512,21515],{"class":5467,"line":5672},[5465,21513,21514],{"class":8155},"            virtual_hosts",[5465,21516,10332],{"class":5489},[5465,21518,21519,21522,21524,21526],{"class":5467,"line":5690},[5465,21520,21521],{"class":5489},"            - ",[5465,21523,6779],{"class":8155},[5465,21525,5807],{"class":5489},[5465,21527,21528],{"class":19483},"backend\n",[5465,21530,21531,21534,21537,21540],{"class":5467,"line":5699},[5465,21532,21533],{"class":8155},"              domains",[5465,21535,21536],{"class":5489},": [",[5465,21538,21539],{"class":5503},"\"*\"",[5465,21541,21542],{"class":5489},"]\n",[5465,21544,21545,21548],{"class":5467,"line":5705},[5465,21546,21547],{"class":8155},"              routes",[5465,21549,10332],{"class":5489},[5465,21551,21552,21555,21558,21560,21563,21565,21568],{"class":5467,"line":5727},[5465,21553,21554],{"class":5489},"              - ",[5465,21556,21557],{"class":8155},"match",[5465,21559,21406],{"class":5489},[5465,21561,21562],{"class":8155},"prefix",[5465,21564,5807],{"class":5489},[5465,21566,21567],{"class":5503},"\"\u002F\"",[5465,21569,21427],{"class":5489},[5465,21571,21572,21575],{"class":5467,"line":5732},[5465,21573,21574],{"class":8155},"                route",[5465,21576,10332],{"class":5489},[5465,21578,21579,21582,21584],{"class":5467,"line":5755},[5465,21580,21581],{"class":8155},"                  cluster",[5465,21583,5807],{"class":5489},[5465,21585,21586],{"class":19483},"grpc_cluster\n",[5465,21588,21589,21592,21594],{"class":5467,"line":5786},[5465,21590,21591],{"class":8155},"                  timeout",[5465,21593,5807],{"class":5489},[5465,21595,21596],{"class":19483},"5s\n",[5465,21598,21599,21602],{"class":5467,"line":5831},[5465,21600,21601],{"class":8155},"          http_filters",[5465,21603,10332],{"class":5489},[5465,21605,21606,21609,21611,21613],{"class":5467,"line":5836},[5465,21607,21608],{"class":5489},"          - ",[5465,21610,6779],{"class":8155},[5465,21612,5807],{"class":5489},[5465,21614,21615],{"class":19483},"envoy.filters.http.router\n",[5465,21617,21618],{"class":5467,"line":5841},[5465,21619,5529],{"emptyLinePlaceholder":5528},[5465,21621,21622,21625],{"class":5467,"line":5850},[5465,21623,21624],{"class":8155},"  clusters",[5465,21626,10332],{"class":5489},[5465,21628,21629,21631,21633,21635],{"class":5467,"line":6213},[5465,21630,21384],{"class":5489},[5465,21632,6779],{"class":8155},[5465,21634,5807],{"class":5489},[5465,21636,21586],{"class":19483},[5465,21638,21639,21642,21644],{"class":5467,"line":6218},[5465,21640,21641],{"class":8155},"    type",[5465,21643,5807],{"class":5489},[5465,21645,21646],{"class":19483},"STRICT_DNS\n",[5465,21648,21649,21652,21654],{"class":5467,"line":6224},[5465,21650,21651],{"class":8155},"    lb_policy",[5465,21653,5807],{"class":5489},[5465,21655,21656],{"class":19483},"ROUND_ROBIN\n",[5465,21658,21659,21662],{"class":5467,"line":6230},[5465,21660,21661],{"class":8155},"    typed_extension_protocol_options",[5465,21663,10332],{"class":5489},[5465,21665,21666,21669],{"class":5467,"line":6236},[5465,21667,21668],{"class":8155},"      envoy.extensions.upstreams.http.v3.HttpProtocolOptions",[5465,21670,10332],{"class":5489},[5465,21672,21673,21676,21678],{"class":5467,"line":6242},[5465,21674,21675],{"class":5503},"        \"@type\"",[5465,21677,5807],{"class":5489},[5465,21679,21680],{"class":19483},"type.googleapis.com\u002Fenvoy.extensions.upstreams.http.v3.HttpProtocolOptions\n",[5465,21682,21683,21686],{"class":5467,"line":6248},[5465,21684,21685],{"class":8155},"        explicit_http_config",[5465,21687,10332],{"class":5489},[5465,21689,21690,21693],{"class":5467,"line":6254},[5465,21691,21692],{"class":8155},"          http2_protocol_options",[5465,21694,21695],{"class":5489},": {}\n",[5465,21697,21698,21701],{"class":5467,"line":6260},[5465,21699,21700],{"class":8155},"    load_assignment",[5465,21702,10332],{"class":5489},[5465,21704,21705,21708,21710],{"class":5467,"line":6265},[5465,21706,21707],{"class":8155},"      cluster_name",[5465,21709,5807],{"class":5489},[5465,21711,21586],{"class":19483},[5465,21713,21714,21717],{"class":5467,"line":6270},[5465,21715,21716],{"class":8155},"      endpoints",[5465,21718,10332],{"class":5489},[5465,21720,21721,21723,21726],{"class":5467,"line":6276},[5465,21722,21448],{"class":5489},[5465,21724,21725],{"class":8155},"lb_endpoints",[5465,21727,10332],{"class":5489},[5465,21729,21730,21733,21736],{"class":5467,"line":6282},[5465,21731,21732],{"class":5489},"        - ",[5465,21734,21735],{"class":8155},"endpoint",[5465,21737,10332],{"class":5489},[5465,21739,21740,21743],{"class":5467,"line":6288},[5465,21741,21742],{"class":8155},"            address",[5465,21744,10332],{"class":5489},[5465,21746,21747,21750,21752,21754,21756,21758,21760,21762,21764,21766],{"class":5467,"line":6294},[5465,21748,21749],{"class":8155},"              socket_address",[5465,21751,21406],{"class":5489},[5465,21753,21409],{"class":8155},[5465,21755,5807],{"class":5489},[5465,21757,5883],{"class":19483},[5465,21759,5336],{"class":5489},[5465,21761,21419],{"class":8155},[5465,21763,5807],{"class":5489},[5465,21765,21424],{"class":7522},[5465,21767,21427],{"class":5489},[5280,21769,21770,6758,21772],{},[5232,21771,7435],{},[7379,21773,21776],{"href":21774,"rel":21775},"https:\u002F\u002Fwww.envoyproxy.io\u002Fdocs",[7383],"envoyproxy.io\u002Fdocs",[5280,21778,21779],{},[5232,21780,21781],{},"Linkerd",[5198,21783,21784,21793,21796,21799,21802],{},[5201,21785,21786,6758,21788],{},[5232,21787,19326],{},[7379,21789,21792],{"href":21790,"rel":21791},"https:\u002F\u002Flinkerd.io",[7383],"linkerd.io",[5201,21794,21795],{},"Легкий та швидкий service mesh",[5201,21797,21798],{},"Написаний на Rust (високапродуктивний data plane)",[5201,21800,21801],{},"Простіше встановлення та експлуатація",[5201,21803,21804],{},"Менше можливостей, ніж Istio",[5280,21806,21807],{},[5232,21808,21809],{},"Consul Connect",[5198,21811,21812,21821,21824,21827],{},[5201,21813,21814,6758,21816],{},[5232,21815,19326],{},[7379,21817,21820],{"href":21818,"rel":21819},"https:\u002F\u002Fconsul.io\u002Fdocs\u002Fconnect",[7383],"consul.io\u002Fdocs\u002Fconnect",[5201,21822,21823],{},"Service mesh від HashiCorp",[5201,21825,21826],{},"Мультихмарний (AWS, Azure, GCP, on-premises)",[5201,21828,21829],{},"Інтеграція з Consul service discovery",[5280,21831,21832],{},"::",[5193,21834,21837,21842,21848,21853,21876,21880,21894,21899],{"icon":21835,"title":21836},"i-lucide-workflow","🔄 Як працює Service Mesh",[5280,21838,21839],{},[5232,21840,21841],{},"Архітектура:",[5456,21843,21846],{"className":21844,"code":21845,"language":7862},[7860],"┌──────────────────────────────────────┐\n│   Control Plane (Управління)         │\n│  - Виписування сертифікатів          │\n│  - Конфігурація політик               │\n│  - Збір метрик та трейсів            │\n└─────────────┬────────────────────────┘\n              │\n    ┌─────────┴─────────┬──────────────┐\n    │                   │              │\n┌───▼────┐         ┌───▼────┐    ┌───▼────┐\n│Service A│        │Service B│    │Service C│\n│ ┌────┐  │        │ ┌────┐  │    │ ┌────┐  │\n│ │App │  │        │ │App │  │    │ │App │  │\n│ └─┬──┘  │        │ └─┬──┘  │    │ └─┬──┘  │\n│ ┌─▼────┐│        │ ┌─▼────┐│    │ ┌─▼────┐│\n│ │Sidecar◄───mTLS───►Sidecar◄─mTLS─►Sidecar││\n│ │(Envoy)││        │ │(Envoy)││    │ │(Envoy)││\n│ └──────┘│        │ └──────┘│    │ └──────┘│\n└─────────┘        └─────────┘    └─────────┘\n",[5211,21847,21845],{"__ignoreMap":5461},[5280,21849,21850],{},[5232,21851,21852],{},"Процес комунікації з Service Mesh:",[6403,21854,21855,21858,21861,21864,21867,21870,21873],{},[5201,21856,21857],{},"Сервіс A викликає gRPC метод Сервісу B",[5201,21859,21860],{},"Запит перехоплюється локальним sidecar proxy",[5201,21862,21863],{},"Sidecar встановлює mTLS з'єднання з sidecar Сервісу B",[5201,21865,21866],{},"Sidecar Сервісу B перевіряє авторизацію",[5201,21868,21869],{},"Якщо дозволено, запит передається локально до Сервісу B",[5201,21871,21872],{},"Відповідь повертається через sidecar'и назад до Сервісу A",[5201,21874,21875],{},"Усі метрики та трейси автоматично збираються",[5280,21877,21878],{},[5232,21879,17325],{},[5198,21881,21882,21885,21888,21891],{},[5201,21883,21884],{},"Нульові зміни коду сервісів (network transparency)",[5201,21886,21887],{},"Централізоване управління політиками безпеки",[5201,21889,21890],{},"Автоматична обробка помилок та retries",[5201,21892,21893],{},"Візуалізація та моніторинг «із коробки»",[5280,21895,21896],{},[5232,21897,21898],{},"Недоліки:",[5198,21900,21901,21904,21907],{},[5201,21902,21903],{},"Додаткова латентність (sidecar proxy overhead: 1–5 мс)",[5201,21905,21906],{},"Збільшене споживання ресурсів (CPU, пам'ять)",[5201,21908,21909],{},"Складність налаштування та траблшутингу",[5193,21911,21914,21980,21985,21990,22001,22006,22017,22022],{"icon":21912,"title":21913},"i-lucide-git-compare","📊 Порівняння підходів mTLS",[6855,21915,21916,21932],{},[6858,21917,21918],{},[6861,21919,21920,21923,21926,21929],{},[6864,21921,21922],{},"Підхід",[6864,21924,21925],{},"Складність",[6864,21927,21928],{},"Продуктивність",[6864,21930,21931],{},"Гнучкість",[6877,21933,21934,21950,21964],{},[6861,21935,21936,21941,21944,21947],{},[6882,21937,21938],{},[5232,21939,21940],{},"Ручна реалізація у коді",[6882,21942,21943],{},"Висока",[6882,21945,21946],{},"Найвища",[6882,21948,21949],{},"Максимальна",[6861,21951,21952,21957,21960,21962],{},[6882,21953,21954],{},[5232,21955,21956],{},"gRPC credentials API",[6882,21958,21959],{},"Середня",[6882,21961,21943],{},[6882,21963,21943],{},[6861,21965,21966,21971,21974,21977],{},[6882,21967,21968],{},[5232,21969,21970],{},"Service Mesh (Istio\u002FLinkerd)",[6882,21972,21973],{},"Низька (для розробника)",[6882,21975,21976],{},"Середня (-5–10%)",[6882,21978,21979],{},"Низька",[5280,21981,21982],{},[5232,21983,21984],{},"Рекомендації:",[5280,21986,21987],{},[5232,21988,21989],{},"Використовуйте ручну реалізацію, якщо:",[5198,21991,21992,21995,21998],{},[5201,21993,21994],{},"Один або кілька критичних сервісів із екстремальними вимогами до латентності",[5201,21996,21997],{},"Потрібен повний контроль над процесом автентифікації",[5201,21999,22000],{},"Немає ресурсів на експлуатацію Service Mesh",[5280,22002,22003],{},[5232,22004,22005],{},"Використовуйте gRPC credentials API, якщо:",[5198,22007,22008,22011,22014],{},[5201,22009,22010],{},"Середня кількість сервісів (5–20)",[5201,22012,22013],{},"Статична конфігурація сертифікатів",[5201,22015,22016],{},"Потрібен баланс між контролем та простотою",[5280,22018,22019],{},[5232,22020,22021],{},"Використовуйте Service Mesh, якщо:",[5198,22023,22024,22027,22030,22033],{},[5201,22025,22026],{},"Велика кількість мікросервісів (20+)",[5201,22028,22029],{},"Потрібна централізована обсервабельність",[5201,22031,22032],{},"Команда має Kubernetes-експертизу",[5201,22034,22035],{},"Критична автоматична ротація сертифікатів",[5280,22037,21832],{},[5267,22039],{},[5270,22041,22043],{"id":22042},"моніторинг-та-спостережуваність-grpc-сервісів","Моніторинг та спостережуваність gRPC-сервісів",[5190,22045,22046,22602,23090],{},[5193,22047,22050,22057,22062,22067,22072,22104,22109,22129,22133,22156,22161,22415,22420,22507,22512,22594],{"icon":22048,"title":22049},"i-lucide-bar-chart","📊 Prometheus + gRPC Metrics",[5280,22051,22052,6758,22054],{},[5232,22053,7417],{},[5211,22055,22056],{},"github.com\u002Fgrpc-ecosystem\u002Fgo-grpc-prometheus",[5280,22058,22059,22061],{},[5232,22060,7395],{}," Автоматичний експорт метрик gRPC у форматі Prometheus",[5280,22063,22064],{},[5232,22065,22066],{},"Основні метрики:",[5280,22068,22069],{},[5232,22070,22071],{},"Server-side:",[5198,22073,22074,22080,22086,22092,22098],{},[5201,22075,22076,22079],{},[5211,22077,22078],{},"grpc_server_started_total"," — загальна кількість початих RPC",[5201,22081,22082,22085],{},[5211,22083,22084],{},"grpc_server_handled_total"," — завершені RPC з кодами статусу",[5201,22087,22088,22091],{},[5211,22089,22090],{},"grpc_server_handling_seconds"," — гістограма тривалості обробки",[5201,22093,22094,22097],{},[5211,22095,22096],{},"grpc_server_msg_received_total"," — кількість отриманих повідомлень",[5201,22099,22100,22103],{},[5211,22101,22102],{},"grpc_server_msg_sent_total"," — кількість відправлених повідомлень",[5280,22105,22106],{},[5232,22107,22108],{},"Client-side:",[5198,22110,22111,22117,22123],{},[5201,22112,22113,22116],{},[5211,22114,22115],{},"grpc_client_started_total"," — загальна кількість викликів",[5201,22118,22119,22122],{},[5211,22120,22121],{},"grpc_client_handled_total"," — завершені виклики",[5201,22124,22125,22128],{},[5211,22126,22127],{},"grpc_client_handling_seconds"," — латентність викликів",[5280,22130,22131],{},[5232,22132,15065],{},[5456,22134,22136],{"className":7649,"code":22135,"language":7651,"meta":5461,"style":5461},"go get github.com\u002Fgrpc-ecosystem\u002Fgo-grpc-prometheus\ngo get github.com\u002Fprometheus\u002Fclient_golang\u002Fprometheus\u002Fpromhttp\n",[5211,22137,22138,22147],{"__ignoreMap":5461},[5465,22139,22140,22142,22144],{"class":5467,"line":5468},[5465,22141,5460],{"class":5538},[5465,22143,15077],{"class":5503},[5465,22145,22146],{"class":5503}," github.com\u002Fgrpc-ecosystem\u002Fgo-grpc-prometheus\n",[5465,22148,22149,22151,22153],{"class":5467,"line":5475},[5465,22150,5460],{"class":5538},[5465,22152,15077],{"class":5503},[5465,22154,22155],{"class":5503}," github.com\u002Fprometheus\u002Fclient_golang\u002Fprometheus\u002Fpromhttp\n",[5280,22157,22158],{},[5232,22159,22160],{},"Приклад серверної інтеграції:",[5456,22162,22164],{"className":5458,"code":22163,"language":5460,"meta":5461,"style":5461},"import (\n    grpc_prometheus \"github.com\u002Fgrpc-ecosystem\u002Fgo-grpc-prometheus\"\n    \"github.com\u002Fprometheus\u002Fclient_golang\u002Fprometheus\u002Fpromhttp\"\n    \"google.golang.org\u002Fgrpc\"\n)\n\nfunc main() {\n    \u002F\u002F Створюємо gRPC сервер з Prometheus інтерцептором\n    server := grpc.NewServer(\n        grpc.ChainUnaryInterceptor(\n            grpc_prometheus.UnaryServerInterceptor,\n        ),\n        grpc.ChainStreamInterceptor(\n            grpc_prometheus.StreamServerInterceptor,\n        ),\n    )\n    \n    \u002F\u002F Реєструємо сервіси\n    pb.RegisterUserServiceServer(server, &userService{})\n    \n    \u002F\u002F Ініціалізуємо метрики\n    grpc_prometheus.Register(server)\n    \n    \u002F\u002F Експортуємо метрики через HTTP endpoint\n    http.Handle(\"\u002Fmetrics\", promhttp.Handler())\n    go http.ListenAndServe(\":9090\", nil)\n    \n    \u002F\u002F Запускаємо gRPC сервер\n    server.Serve(listener)\n}\n",[5211,22165,22166,22172,22180,22185,22189,22193,22197,22205,22210,22224,22234,22245,22249,22260,22271,22275,22279,22283,22288,22306,22310,22315,22329,22333,22338,22365,22388,22392,22397,22411],{"__ignoreMap":5461},[5465,22167,22168,22170],{"class":5467,"line":5468},[5465,22169,9812],{"class":5675},[5465,22171,7535],{"class":5489},[5465,22173,22174,22177],{"class":5467,"line":5475},[5465,22175,22176],{"class":5496},"    grpc_prometheus",[5465,22178,22179],{"class":5503}," \"github.com\u002Fgrpc-ecosystem\u002Fgo-grpc-prometheus\"\n",[5465,22181,22182],{"class":5467,"line":5493},[5465,22183,22184],{"class":5503},"    \"github.com\u002Fprometheus\u002Fclient_golang\u002Fprometheus\u002Fpromhttp\"\n",[5465,22186,22187],{"class":5467,"line":5507},[5465,22188,11246],{"class":5503},[5465,22190,22191],{"class":5467,"line":5519},[5465,22192,5591],{"class":5489},[5465,22194,22195],{"class":5467,"line":5525},[5465,22196,5529],{"emptyLinePlaceholder":5528},[5465,22198,22199,22201,22203],{"class":5467,"line":5532},[5465,22200,5535],{"class":5478},[5465,22202,11266],{"class":5538},[5465,22204,11269],{"class":5489},[5465,22206,22207],{"class":5467,"line":5565},[5465,22208,22209],{"class":5471},"    \u002F\u002F Створюємо gRPC сервер з Prometheus інтерцептором\n",[5465,22211,22212,22214,22216,22218,22220,22222],{"class":5467,"line":5594},[5465,22213,16148],{"class":5496},[5465,22215,5576],{"class":5489},[5465,22217,7555],{"class":5496},[5465,22219,5214],{"class":5489},[5465,22221,11368],{"class":5538},[5465,22223,5621],{"class":5489},[5465,22225,22226,22228,22230,22232],{"class":5467,"line":5600},[5465,22227,11375],{"class":5496},[5465,22229,5214],{"class":5489},[5465,22231,16696],{"class":5538},[5465,22233,5621],{"class":5489},[5465,22235,22236,22239,22241,22243],{"class":5467,"line":5624},[5465,22237,22238],{"class":5496},"            grpc_prometheus",[5465,22240,5214],{"class":5489},[5465,22242,15715],{"class":5496},[5465,22244,5636],{"class":5489},[5465,22246,22247],{"class":5467,"line":5639},[5465,22248,16734],{"class":5489},[5465,22250,22251,22253,22255,22258],{"class":5467,"line":5647},[5465,22252,11375],{"class":5496},[5465,22254,5214],{"class":5489},[5465,22256,22257],{"class":5538},"ChainStreamInterceptor",[5465,22259,5621],{"class":5489},[5465,22261,22262,22264,22266,22269],{"class":5467,"line":5666},[5465,22263,22238],{"class":5496},[5465,22265,5214],{"class":5489},[5465,22267,22268],{"class":5496},"StreamServerInterceptor",[5465,22270,5636],{"class":5489},[5465,22272,22273],{"class":5467,"line":5672},[5465,22274,16734],{"class":5489},[5465,22276,22277],{"class":5467,"line":5690},[5465,22278,5669],{"class":5489},[5465,22280,22281],{"class":5467,"line":5699},[5465,22282,5597],{"class":5489},[5465,22284,22285],{"class":5467,"line":5705},[5465,22286,22287],{"class":5471},"    \u002F\u002F Реєструємо сервіси\n",[5465,22289,22290,22292,22294,22296,22298,22300,22302,22304],{"class":5467,"line":5727},[5465,22291,9843],{"class":5496},[5465,22293,5214],{"class":5489},[5465,22295,11461],{"class":5538},[5465,22297,5542],{"class":5489},[5465,22299,17085],{"class":5496},[5465,22301,5920],{"class":5489},[5465,22303,11471],{"class":5482},[5465,22305,17465],{"class":5489},[5465,22307,22308],{"class":5467,"line":5732},[5465,22309,5597],{"class":5489},[5465,22311,22312],{"class":5467,"line":5755},[5465,22313,22314],{"class":5471},"    \u002F\u002F Ініціалізуємо метрики\n",[5465,22316,22317,22319,22321,22323,22325,22327],{"class":5467,"line":5786},[5465,22318,22176],{"class":5496},[5465,22320,5214],{"class":5489},[5465,22322,11492],{"class":5538},[5465,22324,5542],{"class":5489},[5465,22326,17085],{"class":5496},[5465,22328,5591],{"class":5489},[5465,22330,22331],{"class":5467,"line":5831},[5465,22332,5597],{"class":5489},[5465,22334,22335],{"class":5467,"line":5836},[5465,22336,22337],{"class":5471},"    \u002F\u002F Експортуємо метрики через HTTP endpoint\n",[5465,22339,22340,22343,22345,22348,22350,22353,22355,22358,22360,22363],{"class":5467,"line":5841},[5465,22341,22342],{"class":5496},"    http",[5465,22344,5214],{"class":5489},[5465,22346,22347],{"class":5538},"Handle",[5465,22349,5542],{"class":5489},[5465,22351,22352],{"class":5503},"\"\u002Fmetrics\"",[5465,22354,5336],{"class":5489},[5465,22356,22357],{"class":5496},"promhttp",[5465,22359,5214],{"class":5489},[5465,22361,22362],{"class":5538},"Handler",[5465,22364,12206],{"class":5489},[5465,22366,22367,22369,22372,22374,22377,22379,22382,22384,22386],{"class":5467,"line":5850},[5465,22368,17470],{"class":5675},[5465,22370,22371],{"class":5496}," http",[5465,22373,5214],{"class":5489},[5465,22375,22376],{"class":5538},"ListenAndServe",[5465,22378,5542],{"class":5489},[5465,22380,22381],{"class":5503},"\":9090\"",[5465,22383,5336],{"class":5489},[5465,22385,5685],{"class":5478},[5465,22387,5591],{"class":5489},[5465,22389,22390],{"class":5467,"line":6213},[5465,22391,5597],{"class":5489},[5465,22393,22394],{"class":5467,"line":6218},[5465,22395,22396],{"class":5471},"    \u002F\u002F Запускаємо gRPC сервер\n",[5465,22398,22399,22401,22403,22405,22407,22409],{"class":5467,"line":6224},[5465,22400,16148],{"class":5496},[5465,22402,5214],{"class":5489},[5465,22404,11541],{"class":5538},[5465,22406,5542],{"class":5489},[5465,22408,11546],{"class":5496},[5465,22410,5591],{"class":5489},[5465,22412,22413],{"class":5467,"line":6230},[5465,22414,5522],{"class":5489},[5280,22416,22417],{},[5232,22418,22419],{},"Приклад клієнтської інтеграції:",[5456,22421,22423],{"className":5458,"code":22422,"language":5460,"meta":5461,"style":5461},"conn, err := grpc.Dial(\n    \"localhost:50051\",\n    grpc.WithUnaryInterceptor(\n        grpc_prometheus.UnaryClientInterceptor,\n    ),\n    grpc.WithStreamInterceptor(\n        grpc_prometheus.StreamClientInterceptor,\n    ),\n)\n",[5211,22424,22425,22443,22450,22461,22473,22477,22488,22499,22503],{"__ignoreMap":5461},[5465,22426,22427,22429,22431,22433,22435,22437,22439,22441],{"class":5467,"line":5468},[5465,22428,11889],{"class":5496},[5465,22430,5336],{"class":5489},[5465,22432,5608],{"class":5496},[5465,22434,5576],{"class":5489},[5465,22436,7555],{"class":5496},[5465,22438,5214],{"class":5489},[5465,22440,11756],{"class":5538},[5465,22442,5621],{"class":5489},[5465,22444,22445,22448],{"class":5467,"line":5475},[5465,22446,22447],{"class":5503},"    \"localhost:50051\"",[5465,22449,5636],{"class":5489},[5465,22451,22452,22454,22456,22459],{"class":5467,"line":5493},[5465,22453,17100],{"class":5496},[5465,22455,5214],{"class":5489},[5465,22457,22458],{"class":5538},"WithUnaryInterceptor",[5465,22460,5621],{"class":5489},[5465,22462,22463,22466,22468,22471],{"class":5467,"line":5507},[5465,22464,22465],{"class":5496},"        grpc_prometheus",[5465,22467,5214],{"class":5489},[5465,22469,22470],{"class":5496},"UnaryClientInterceptor",[5465,22472,5636],{"class":5489},[5465,22474,22475],{"class":5467,"line":5519},[5465,22476,17138],{"class":5489},[5465,22478,22479,22481,22483,22486],{"class":5467,"line":5525},[5465,22480,17100],{"class":5496},[5465,22482,5214],{"class":5489},[5465,22484,22485],{"class":5538},"WithStreamInterceptor",[5465,22487,5621],{"class":5489},[5465,22489,22490,22492,22494,22497],{"class":5467,"line":5532},[5465,22491,22465],{"class":5496},[5465,22493,5214],{"class":5489},[5465,22495,22496],{"class":5496},"StreamClientInterceptor",[5465,22498,5636],{"class":5489},[5465,22500,22501],{"class":5467,"line":5565},[5465,22502,17138],{"class":5489},[5465,22504,22505],{"class":5467,"line":5594},[5465,22506,5591],{"class":5489},[5280,22508,22509],{},[5232,22510,22511],{},"Prometheus queries приклади:",[5456,22513,22517],{"className":22514,"code":22515,"language":22516,"meta":5461,"style":5461},"language-promql shiki shiki-themes light-plus dark-plus dark-plus","# RPS (Requests Per Second)\nrate(grpc_server_handled_total[1m])\n\n# Success rate\nsum(rate(grpc_server_handled_total{grpc_code=\"OK\"}[5m])) \u002F \nsum(rate(grpc_server_handled_total[5m]))\n\n# 99th percentile latency\nhistogram_quantile(0.99, \n  rate(grpc_server_handling_seconds_bucket[5m])\n)\n\n# Error rate by method\nsum by (grpc_method) (\n  rate(grpc_server_handled_total{grpc_code!=\"OK\"}[5m])\n)\n","promql",[5211,22518,22519,22524,22529,22533,22538,22543,22548,22552,22557,22562,22567,22571,22575,22580,22585,22590],{"__ignoreMap":5461},[5465,22520,22521],{"class":5467,"line":5468},[5465,22522,22523],{},"# RPS (Requests Per Second)\n",[5465,22525,22526],{"class":5467,"line":5475},[5465,22527,22528],{},"rate(grpc_server_handled_total[1m])\n",[5465,22530,22531],{"class":5467,"line":5493},[5465,22532,5529],{"emptyLinePlaceholder":5528},[5465,22534,22535],{"class":5467,"line":5507},[5465,22536,22537],{},"# Success rate\n",[5465,22539,22540],{"class":5467,"line":5519},[5465,22541,22542],{},"sum(rate(grpc_server_handled_total{grpc_code=\"OK\"}[5m])) \u002F \n",[5465,22544,22545],{"class":5467,"line":5525},[5465,22546,22547],{},"sum(rate(grpc_server_handled_total[5m]))\n",[5465,22549,22550],{"class":5467,"line":5532},[5465,22551,5529],{"emptyLinePlaceholder":5528},[5465,22553,22554],{"class":5467,"line":5565},[5465,22555,22556],{},"# 99th percentile latency\n",[5465,22558,22559],{"class":5467,"line":5594},[5465,22560,22561],{},"histogram_quantile(0.99, \n",[5465,22563,22564],{"class":5467,"line":5600},[5465,22565,22566],{},"  rate(grpc_server_handling_seconds_bucket[5m])\n",[5465,22568,22569],{"class":5467,"line":5624},[5465,22570,5591],{},[5465,22572,22573],{"class":5467,"line":5639},[5465,22574,5529],{"emptyLinePlaceholder":5528},[5465,22576,22577],{"class":5467,"line":5647},[5465,22578,22579],{},"# Error rate by method\n",[5465,22581,22582],{"class":5467,"line":5666},[5465,22583,22584],{},"sum by (grpc_method) (\n",[5465,22586,22587],{"class":5467,"line":5672},[5465,22588,22589],{},"  rate(grpc_server_handled_total{grpc_code!=\"OK\"}[5m])\n",[5465,22591,22592],{"class":5467,"line":5690},[5465,22593,5591],{},[5280,22595,22596,6758,22598],{},[5232,22597,7435],{},[7379,22599,22056],{"href":22600,"rel":22601},"https:\u002F\u002Fgithub.com\u002Fgrpc-ecosystem\u002Fgo-grpc-prometheus",[7383],[5193,22603,22605,22612,22617,22622,22636,22640,22672,22677,23062,23067,23081],{"icon":1348,"title":22604},"🔍 OpenTelemetry для gRPC",[5280,22606,22607,6758,22609],{},[5232,22608,7417],{},[5211,22610,22611],{},"go.opentelemetry.io\u002Fcontrib\u002Finstrumentation\u002Fgoogle.golang.org\u002Fgrpc\u002Fotelgrpc",[5280,22613,22614,22616],{},[5232,22615,7395],{}," Розподілений трейсинг та метрики через OpenTelemetry стандарт",[5280,22618,22619],{},[5232,22620,22621],{},"Можливості:",[5198,22623,22624,22627,22630,22633],{},[5201,22625,22626],{},"Автоматичне створення span'ів для кожного RPC",[5201,22628,22629],{},"Propagation контексту між сервісами",[5201,22631,22632],{},"Інтеграція з Jaeger, Zipkin, Google Cloud Trace",[5201,22634,22635],{},"Збір метрик у OpenTelemetry форматі",[5280,22637,22638],{},[5232,22639,15065],{},[5456,22641,22643],{"className":7649,"code":22642,"language":7651,"meta":5461,"style":5461},"go get go.opentelemetry.io\u002Fcontrib\u002Finstrumentation\u002Fgoogle.golang.org\u002Fgrpc\u002Fotelgrpc\ngo get go.opentelemetry.io\u002Fotel\ngo get go.opentelemetry.io\u002Fotel\u002Fexporters\u002Fjaeger\n",[5211,22644,22645,22654,22663],{"__ignoreMap":5461},[5465,22646,22647,22649,22651],{"class":5467,"line":5468},[5465,22648,5460],{"class":5538},[5465,22650,15077],{"class":5503},[5465,22652,22653],{"class":5503}," go.opentelemetry.io\u002Fcontrib\u002Finstrumentation\u002Fgoogle.golang.org\u002Fgrpc\u002Fotelgrpc\n",[5465,22655,22656,22658,22660],{"class":5467,"line":5475},[5465,22657,5460],{"class":5538},[5465,22659,15077],{"class":5503},[5465,22661,22662],{"class":5503}," go.opentelemetry.io\u002Fotel\n",[5465,22664,22665,22667,22669],{"class":5467,"line":5493},[5465,22666,5460],{"class":5538},[5465,22668,15077],{"class":5503},[5465,22670,22671],{"class":5503}," go.opentelemetry.io\u002Fotel\u002Fexporters\u002Fjaeger\n",[5280,22673,22674],{},[5232,22675,22676],{},"Приклад інтеграції:",[5456,22678,22680],{"className":5458,"code":22679,"language":5460,"meta":5461,"style":5461},"import (\n    \"go.opentelemetry.io\u002Fcontrib\u002Finstrumentation\u002Fgoogle.golang.org\u002Fgrpc\u002Fotelgrpc\"\n    \"go.opentelemetry.io\u002Fotel\"\n)\n\nfunc main() {\n    \u002F\u002F Ініціалізація OpenTelemetry tracer\n    tp := initTracer()\n    defer tp.Shutdown(context.Background())\n    \n    \u002F\u002F gRPC сервер з трейсингом\n    server := grpc.NewServer(\n        grpc.StatsHandler(otelgrpc.NewServerHandler()),\n    )\n    \n    \u002F\u002F gRPC клієнт з трейсингом\n    conn, _ := grpc.Dial(\n        \"localhost:50051\",\n        grpc.WithStatsHandler(otelgrpc.NewClientHandler()),\n    )\n}\n\nfunc initTracer() *sdktrace.TracerProvider {\n    exporter, _ := jaeger.New(jaeger.WithCollectorEndpoint(\n        jaeger.WithEndpoint(\"http:\u002F\u002Flocalhost:14268\u002Fapi\u002Ftraces\"),\n    ))\n    \n    tp := sdktrace.NewTracerProvider(\n        sdktrace.WithBatcher(exporter),\n        sdktrace.WithResource(resource.NewWithAttributes(\n            semconv.SchemaURL,\n            semconv.ServiceName(\"userservice\"),\n        )),\n    )\n    \n    otel.SetTracerProvider(tp)\n    return tp\n}\n",[5211,22681,22682,22688,22693,22698,22702,22706,22714,22719,22731,22753,22757,22762,22776,22797,22801,22805,22810,22828,22834,22854,22858,22862,22866,22885,22915,22932,22937,22941,22956,22973,22994,23006,23021,23026,23030,23034,23051,23058],{"__ignoreMap":5461},[5465,22683,22684,22686],{"class":5467,"line":5468},[5465,22685,9812],{"class":5675},[5465,22687,7535],{"class":5489},[5465,22689,22690],{"class":5467,"line":5475},[5465,22691,22692],{"class":5503},"    \"go.opentelemetry.io\u002Fcontrib\u002Finstrumentation\u002Fgoogle.golang.org\u002Fgrpc\u002Fotelgrpc\"\n",[5465,22694,22695],{"class":5467,"line":5493},[5465,22696,22697],{"class":5503},"    \"go.opentelemetry.io\u002Fotel\"\n",[5465,22699,22700],{"class":5467,"line":5507},[5465,22701,5591],{"class":5489},[5465,22703,22704],{"class":5467,"line":5519},[5465,22705,5529],{"emptyLinePlaceholder":5528},[5465,22707,22708,22710,22712],{"class":5467,"line":5525},[5465,22709,5535],{"class":5478},[5465,22711,11266],{"class":5538},[5465,22713,11269],{"class":5489},[5465,22715,22716],{"class":5467,"line":5532},[5465,22717,22718],{"class":5471},"    \u002F\u002F Ініціалізація OpenTelemetry tracer\n",[5465,22720,22721,22724,22726,22729],{"class":5467,"line":5565},[5465,22722,22723],{"class":5496},"    tp",[5465,22725,5576],{"class":5489},[5465,22727,22728],{"class":5538},"initTracer",[5465,22730,5724],{"class":5489},[5465,22732,22733,22735,22738,22740,22743,22745,22747,22749,22751],{"class":5467,"line":5594},[5465,22734,5708],{"class":5675},[5465,22736,22737],{"class":5496}," tp",[5465,22739,5214],{"class":5489},[5465,22741,22742],{"class":5538},"Shutdown",[5465,22744,5542],{"class":5489},[5465,22746,11969],{"class":5496},[5465,22748,5214],{"class":5489},[5465,22750,11983],{"class":5538},[5465,22752,12206],{"class":5489},[5465,22754,22755],{"class":5467,"line":5600},[5465,22756,5597],{"class":5489},[5465,22758,22759],{"class":5467,"line":5624},[5465,22760,22761],{"class":5471},"    \u002F\u002F gRPC сервер з трейсингом\n",[5465,22763,22764,22766,22768,22770,22772,22774],{"class":5467,"line":5639},[5465,22765,16148],{"class":5496},[5465,22767,5576],{"class":5489},[5465,22769,7555],{"class":5496},[5465,22771,5214],{"class":5489},[5465,22773,11368],{"class":5538},[5465,22775,5621],{"class":5489},[5465,22777,22778,22780,22782,22785,22787,22790,22792,22795],{"class":5467,"line":5647},[5465,22779,11375],{"class":5496},[5465,22781,5214],{"class":5489},[5465,22783,22784],{"class":5538},"StatsHandler",[5465,22786,5542],{"class":5489},[5465,22788,22789],{"class":5496},"otelgrpc",[5465,22791,5214],{"class":5489},[5465,22793,22794],{"class":5538},"NewServerHandler",[5465,22796,15298],{"class":5489},[5465,22798,22799],{"class":5467,"line":5666},[5465,22800,5669],{"class":5489},[5465,22802,22803],{"class":5467,"line":5672},[5465,22804,5597],{"class":5489},[5465,22806,22807],{"class":5467,"line":5690},[5465,22808,22809],{"class":5471},"    \u002F\u002F gRPC клієнт з трейсингом\n",[5465,22811,22812,22814,22816,22818,22820,22822,22824,22826],{"class":5467,"line":5699},[5465,22813,11743],{"class":5496},[5465,22815,5336],{"class":5489},[5465,22817,5573],{"class":5496},[5465,22819,5576],{"class":5489},[5465,22821,7555],{"class":5496},[5465,22823,5214],{"class":5489},[5465,22825,11756],{"class":5538},[5465,22827,5621],{"class":5489},[5465,22829,22830,22832],{"class":5467,"line":5705},[5465,22831,11763],{"class":5503},[5465,22833,5636],{"class":5489},[5465,22835,22836,22838,22840,22843,22845,22847,22849,22852],{"class":5467,"line":5727},[5465,22837,11375],{"class":5496},[5465,22839,5214],{"class":5489},[5465,22841,22842],{"class":5538},"WithStatsHandler",[5465,22844,5542],{"class":5489},[5465,22846,22789],{"class":5496},[5465,22848,5214],{"class":5489},[5465,22850,22851],{"class":5538},"NewClientHandler",[5465,22853,15298],{"class":5489},[5465,22855,22856],{"class":5467,"line":5732},[5465,22857,5669],{"class":5489},[5465,22859,22860],{"class":5467,"line":5755},[5465,22861,5522],{"class":5489},[5465,22863,22864],{"class":5467,"line":5786},[5465,22865,5529],{"emptyLinePlaceholder":5528},[5465,22867,22868,22870,22873,22875,22878,22880,22883],{"class":5467,"line":5831},[5465,22869,5535],{"class":5478},[5465,22871,22872],{"class":5538}," initTracer",[5465,22874,9966],{"class":5489},[5465,22876,22877],{"class":5482},"sdktrace",[5465,22879,5214],{"class":5489},[5465,22881,22882],{"class":5482},"TracerProvider",[5465,22884,5490],{"class":5489},[5465,22886,22887,22890,22892,22894,22896,22899,22901,22904,22906,22908,22910,22913],{"class":5467,"line":5836},[5465,22888,22889],{"class":5496},"    exporter",[5465,22891,5336],{"class":5489},[5465,22893,5573],{"class":5496},[5465,22895,5576],{"class":5489},[5465,22897,22898],{"class":5496},"jaeger",[5465,22900,5214],{"class":5489},[5465,22902,22903],{"class":5538},"New",[5465,22905,5542],{"class":5489},[5465,22907,22898],{"class":5496},[5465,22909,5214],{"class":5489},[5465,22911,22912],{"class":5538},"WithCollectorEndpoint",[5465,22914,5621],{"class":5489},[5465,22916,22917,22920,22922,22925,22927,22930],{"class":5467,"line":5841},[5465,22918,22919],{"class":5496},"        jaeger",[5465,22921,5214],{"class":5489},[5465,22923,22924],{"class":5538},"WithEndpoint",[5465,22926,5542],{"class":5489},[5465,22928,22929],{"class":5503},"\"http:\u002F\u002Flocalhost:14268\u002Fapi\u002Ftraces\"",[5465,22931,5663],{"class":5489},[5465,22933,22934],{"class":5467,"line":5850},[5465,22935,22936],{"class":5489},"    ))\n",[5465,22938,22939],{"class":5467,"line":6213},[5465,22940,5597],{"class":5489},[5465,22942,22943,22945,22947,22949,22951,22954],{"class":5467,"line":6218},[5465,22944,22723],{"class":5496},[5465,22946,5576],{"class":5489},[5465,22948,22877],{"class":5496},[5465,22950,5214],{"class":5489},[5465,22952,22953],{"class":5538},"NewTracerProvider",[5465,22955,5621],{"class":5489},[5465,22957,22958,22961,22963,22966,22968,22971],{"class":5467,"line":6224},[5465,22959,22960],{"class":5496},"        sdktrace",[5465,22962,5214],{"class":5489},[5465,22964,22965],{"class":5538},"WithBatcher",[5465,22967,5542],{"class":5489},[5465,22969,22970],{"class":5496},"exporter",[5465,22972,5663],{"class":5489},[5465,22974,22975,22977,22979,22982,22984,22987,22989,22992],{"class":5467,"line":6230},[5465,22976,22960],{"class":5496},[5465,22978,5214],{"class":5489},[5465,22980,22981],{"class":5538},"WithResource",[5465,22983,5542],{"class":5489},[5465,22985,22986],{"class":5496},"resource",[5465,22988,5214],{"class":5489},[5465,22990,22991],{"class":5538},"NewWithAttributes",[5465,22993,5621],{"class":5489},[5465,22995,22996,22999,23001,23004],{"class":5467,"line":6236},[5465,22997,22998],{"class":5496},"            semconv",[5465,23000,5214],{"class":5489},[5465,23002,23003],{"class":5496},"SchemaURL",[5465,23005,5636],{"class":5489},[5465,23007,23008,23010,23012,23015,23017,23019],{"class":5467,"line":6242},[5465,23009,22998],{"class":5496},[5465,23011,5214],{"class":5489},[5465,23013,23014],{"class":5538},"ServiceName",[5465,23016,5542],{"class":5489},[5465,23018,15308],{"class":5503},[5465,23020,5663],{"class":5489},[5465,23022,23023],{"class":5467,"line":6248},[5465,23024,23025],{"class":5489},"        )),\n",[5465,23027,23028],{"class":5467,"line":6254},[5465,23029,5669],{"class":5489},[5465,23031,23032],{"class":5467,"line":6260},[5465,23033,5597],{"class":5489},[5465,23035,23036,23039,23041,23044,23046,23049],{"class":5467,"line":6265},[5465,23037,23038],{"class":5496},"    otel",[5465,23040,5214],{"class":5489},[5465,23042,23043],{"class":5538},"SetTracerProvider",[5465,23045,5542],{"class":5489},[5465,23047,23048],{"class":5496},"tp",[5465,23050,5591],{"class":5489},[5465,23052,23053,23055],{"class":5467,"line":6270},[5465,23054,5844],{"class":5675},[5465,23056,23057],{"class":5496}," tp\n",[5465,23059,23060],{"class":5467,"line":6276},[5465,23061,5522],{"class":5489},[5280,23063,23064],{},[5232,23065,23066],{},"Візуалізація в Jaeger:",[5198,23068,23069,23072,23075,23078],{},[5201,23070,23071],{},"Повний ланцюжок викликів між мікросервісами",[5201,23073,23074],{},"Тривалість кожного RPC",[5201,23076,23077],{},"Залежності між сервісами",[5201,23079,23080],{},"Помилки та їх контекст",[5280,23082,23083,6758,23085],{},[5232,23084,7435],{},[7379,23086,23089],{"href":23087,"rel":23088},"https:\u002F\u002Fopentelemetry.io\u002Fdocs\u002Finstrumentation\u002Fgo\u002F",[7383],"opentelemetry.io\u002Fdocs\u002Finstrumentation\u002Fgo",[5193,23091,23094,23102,23107,23112,23719,23724,23796],{"icon":23092,"title":23093},"i-lucide-file-text","📝 Структуроване логування",[5280,23095,23096,6758,23098,23101],{},[5232,23097,7417],{},[5211,23099,23100],{},"log\u002Fslog"," (стандартна бібліотека Go 1.21+)",[5280,23103,23104,23106],{},[5232,23105,7395],{}," Структуроване логування із підтримкою контексту та рівнів",[5280,23108,23109],{},[5232,23110,23111],{},"Інтеграція з gRPC через інтерцептор:",[5456,23113,23115],{"className":5458,"code":23114,"language":5460,"meta":5461,"style":5461},"import (\n    \"log\u002Fslog\"\n    \"os\"\n    \"google.golang.org\u002Fgrpc\"\n)\n\nfunc loggingInterceptor(logger *slog.Logger) grpc.UnaryServerInterceptor {\n    return func(\n        ctx context.Context,\n        req interface{},\n        info *grpc.UnaryServerInfo,\n        handler grpc.UnaryHandler,\n    ) (interface{}, error) {\n        start := time.Now()\n        \n        \u002F\u002F Логуємо початок запиту\n        logger.InfoContext(ctx, \"gRPC request started\",\n            slog.String(\"method\", info.FullMethod),\n        )\n        \n        \u002F\u002F Виконуємо обробник\n        resp, err := handler(ctx, req)\n        \n        \u002F\u002F Логуємо результат\n        duration := time.Since(start)\n        \n        if err != nil {\n            logger.ErrorContext(ctx, \"gRPC request failed\",\n                slog.String(\"method\", info.FullMethod),\n                slog.Duration(\"duration\", duration),\n                slog.String(\"error\", err.Error()),\n            )\n        } else {\n            logger.InfoContext(ctx, \"gRPC request completed\",\n                slog.String(\"method\", info.FullMethod),\n                slog.Duration(\"duration\", duration),\n            )\n        }\n        \n        return resp, err\n    }\n}\n\nfunc main() {\n    \u002F\u002F JSON structured logging\n    logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{\n        Level: slog.LevelInfo,\n    }))\n    \n    server := grpc.NewServer(\n        grpc.UnaryInterceptor(loggingInterceptor(logger)),\n    )\n    \n    \u002F\u002F ... реєстрація сервісів\n}\n",[5211,23116,23117,23123,23128,23132,23136,23140,23144,23174,23182,23194,23203,23218,23231,23244,23259,23263,23268,23289,23314,23319,23323,23328,23350,23354,23359,23378,23382,23394,23415,23438,23459,23482,23487,23496,23515,23537,23555,23559,23563,23567,23577,23581,23585,23589,23597,23602,23644,23660,23665,23669,23683,23703,23707,23711,23715],{"__ignoreMap":5461},[5465,23118,23119,23121],{"class":5467,"line":5468},[5465,23120,9812],{"class":5675},[5465,23122,7535],{"class":5489},[5465,23124,23125],{"class":5467,"line":5475},[5465,23126,23127],{"class":5503},"    \"log\u002Fslog\"\n",[5465,23129,23130],{"class":5467,"line":5493},[5465,23131,20526],{"class":5503},[5465,23133,23134],{"class":5467,"line":5507},[5465,23135,11246],{"class":5503},[5465,23137,23138],{"class":5467,"line":5519},[5465,23139,5591],{"class":5489},[5465,23141,23142],{"class":5467,"line":5525},[5465,23143,5529],{"emptyLinePlaceholder":5528},[5465,23145,23146,23148,23151,23153,23155,23157,23159,23161,23164,23166,23168,23170,23172],{"class":5467,"line":5532},[5465,23147,5535],{"class":5478},[5465,23149,23150],{"class":5538}," loggingInterceptor",[5465,23152,5542],{"class":5489},[5465,23154,17131],{"class":5496},[5465,23156,10122],{"class":5489},[5465,23158,16958],{"class":5482},[5465,23160,5214],{"class":5489},[5465,23162,23163],{"class":5482},"Logger",[5465,23165,5557],{"class":5489},[5465,23167,7555],{"class":5482},[5465,23169,5214],{"class":5489},[5465,23171,15715],{"class":5482},[5465,23173,5490],{"class":5489},[5465,23175,23176,23178,23180],{"class":5467,"line":5565},[5465,23177,5844],{"class":5675},[5465,23179,15724],{"class":5478},[5465,23181,5621],{"class":5489},[5465,23183,23184,23186,23188,23190,23192],{"class":5467,"line":5594},[5465,23185,15857],{"class":5496},[5465,23187,10107],{"class":5482},[5465,23189,5214],{"class":5489},[5465,23191,10112],{"class":5482},[5465,23193,5636],{"class":5489},[5465,23195,23196,23199,23201],{"class":5467,"line":5600},[5465,23197,23198],{"class":5496},"        req",[5465,23200,13799],{"class":5478},[5465,23202,13802],{"class":5489},[5465,23204,23205,23208,23210,23212,23214,23216],{"class":5467,"line":5624},[5465,23206,23207],{"class":5496},"        info",[5465,23209,10122],{"class":5489},[5465,23211,7555],{"class":5482},[5465,23213,5214],{"class":5489},[5465,23215,13816],{"class":5482},[5465,23217,5636],{"class":5489},[5465,23219,23220,23223,23225,23227,23229],{"class":5467,"line":5639},[5465,23221,23222],{"class":5496},"        handler",[5465,23224,13826],{"class":5482},[5465,23226,5214],{"class":5489},[5465,23228,13831],{"class":5482},[5465,23230,5636],{"class":5489},[5465,23232,23233,23236,23238,23240,23242],{"class":5467,"line":5647},[5465,23234,23235],{"class":5489},"    ) (",[5465,23237,13841],{"class":5478},[5465,23239,13844],{"class":5489},[5465,23241,5560],{"class":5482},[5465,23243,10148],{"class":5489},[5465,23245,23246,23249,23251,23253,23255,23257],{"class":5467,"line":5666},[5465,23247,23248],{"class":5496},"        start",[5465,23250,5576],{"class":5489},[5465,23252,11994],{"class":5496},[5465,23254,5214],{"class":5489},[5465,23256,13862],{"class":5538},[5465,23258,5724],{"class":5489},[5465,23260,23261],{"class":5467,"line":5672},[5465,23262,10933],{"class":5489},[5465,23264,23265],{"class":5467,"line":5690},[5465,23266,23267],{"class":5471},"        \u002F\u002F Логуємо початок запиту\n",[5465,23269,23270,23273,23275,23278,23280,23282,23284,23287],{"class":5467,"line":5699},[5465,23271,23272],{"class":5496},"        logger",[5465,23274,5214],{"class":5489},[5465,23276,23277],{"class":5538},"InfoContext",[5465,23279,5542],{"class":5489},[5465,23281,5917],{"class":5496},[5465,23283,5336],{"class":5489},[5465,23285,23286],{"class":5503},"\"gRPC request started\"",[5465,23288,5636],{"class":5489},[5465,23290,23291,23294,23296,23299,23301,23304,23306,23308,23310,23312],{"class":5467,"line":5705},[5465,23292,23293],{"class":5496},"            slog",[5465,23295,5214],{"class":5489},[5465,23297,23298],{"class":5538},"String",[5465,23300,5542],{"class":5489},[5465,23302,23303],{"class":5503},"\"method\"",[5465,23305,5336],{"class":5489},[5465,23307,13890],{"class":5496},[5465,23309,5214],{"class":5489},[5465,23311,13895],{"class":5496},[5465,23313,5663],{"class":5489},[5465,23315,23316],{"class":5467,"line":5727},[5465,23317,23318],{"class":5489},"        )\n",[5465,23320,23321],{"class":5467,"line":5732},[5465,23322,10933],{"class":5489},[5465,23324,23325],{"class":5467,"line":5755},[5465,23326,23327],{"class":5471},"        \u002F\u002F Виконуємо обробник\n",[5465,23329,23330,23332,23334,23336,23338,23340,23342,23344,23346,23348],{"class":5467,"line":5786},[5465,23331,12166],{"class":5496},[5465,23333,5336],{"class":5489},[5465,23335,5608],{"class":5496},[5465,23337,5576],{"class":5489},[5465,23339,13919],{"class":5538},[5465,23341,5542],{"class":5489},[5465,23343,5917],{"class":5496},[5465,23345,5336],{"class":5489},[5465,23347,10429],{"class":5496},[5465,23349,5591],{"class":5489},[5465,23351,23352],{"class":5467,"line":5831},[5465,23353,10933],{"class":5489},[5465,23355,23356],{"class":5467,"line":5836},[5465,23357,23358],{"class":5471},"        \u002F\u002F Логуємо результат\n",[5465,23360,23361,23364,23366,23368,23370,23372,23374,23376],{"class":5467,"line":5841},[5465,23362,23363],{"class":5496},"        duration",[5465,23365,5576],{"class":5489},[5465,23367,11994],{"class":5496},[5465,23369,5214],{"class":5489},[5465,23371,13952],{"class":5538},[5465,23373,5542],{"class":5489},[5465,23375,13957],{"class":5496},[5465,23377,5591],{"class":5489},[5465,23379,23380],{"class":5467,"line":5850},[5465,23381,10933],{"class":5489},[5465,23383,23384,23386,23388,23390,23392],{"class":5467,"line":6213},[5465,23385,10843],{"class":5675},[5465,23387,5679],{"class":5496},[5465,23389,5682],{"class":5489},[5465,23391,5685],{"class":5478},[5465,23393,5490],{"class":5489},[5465,23395,23396,23399,23401,23404,23406,23408,23410,23413],{"class":5467,"line":6218},[5465,23397,23398],{"class":5496},"            logger",[5465,23400,5214],{"class":5489},[5465,23402,23403],{"class":5538},"ErrorContext",[5465,23405,5542],{"class":5489},[5465,23407,5917],{"class":5496},[5465,23409,5336],{"class":5489},[5465,23411,23412],{"class":5503},"\"gRPC request failed\"",[5465,23414,5636],{"class":5489},[5465,23416,23417,23420,23422,23424,23426,23428,23430,23432,23434,23436],{"class":5467,"line":6224},[5465,23418,23419],{"class":5496},"                slog",[5465,23421,5214],{"class":5489},[5465,23423,23298],{"class":5538},[5465,23425,5542],{"class":5489},[5465,23427,23303],{"class":5503},[5465,23429,5336],{"class":5489},[5465,23431,13890],{"class":5496},[5465,23433,5214],{"class":5489},[5465,23435,13895],{"class":5496},[5465,23437,5663],{"class":5489},[5465,23439,23440,23442,23444,23447,23449,23452,23454,23457],{"class":5467,"line":6230},[5465,23441,23419],{"class":5496},[5465,23443,5214],{"class":5489},[5465,23445,23446],{"class":5538},"Duration",[5465,23448,5542],{"class":5489},[5465,23450,23451],{"class":5503},"\"duration\"",[5465,23453,5336],{"class":5489},[5465,23455,23456],{"class":5496},"duration",[5465,23458,5663],{"class":5489},[5465,23460,23461,23463,23465,23467,23469,23472,23474,23476,23478,23480],{"class":5467,"line":6236},[5465,23462,23419],{"class":5496},[5465,23464,5214],{"class":5489},[5465,23466,23298],{"class":5538},[5465,23468,5542],{"class":5489},[5465,23470,23471],{"class":5503},"\"error\"",[5465,23473,5336],{"class":5489},[5465,23475,5608],{"class":5496},[5465,23477,5214],{"class":5489},[5465,23479,10189],{"class":5538},[5465,23481,15298],{"class":5489},[5465,23483,23484],{"class":5467,"line":6242},[5465,23485,23486],{"class":5489},"            )\n",[5465,23488,23489,23492,23494],{"class":5467,"line":6248},[5465,23490,23491],{"class":5489},"        } ",[5465,23493,14078],{"class":5675},[5465,23495,5490],{"class":5489},[5465,23497,23498,23500,23502,23504,23506,23508,23510,23513],{"class":5467,"line":6254},[5465,23499,23398],{"class":5496},[5465,23501,5214],{"class":5489},[5465,23503,23277],{"class":5538},[5465,23505,5542],{"class":5489},[5465,23507,5917],{"class":5496},[5465,23509,5336],{"class":5489},[5465,23511,23512],{"class":5503},"\"gRPC request completed\"",[5465,23514,5636],{"class":5489},[5465,23516,23517,23519,23521,23523,23525,23527,23529,23531,23533,23535],{"class":5467,"line":6260},[5465,23518,23419],{"class":5496},[5465,23520,5214],{"class":5489},[5465,23522,23298],{"class":5538},[5465,23524,5542],{"class":5489},[5465,23526,23303],{"class":5503},[5465,23528,5336],{"class":5489},[5465,23530,13890],{"class":5496},[5465,23532,5214],{"class":5489},[5465,23534,13895],{"class":5496},[5465,23536,5663],{"class":5489},[5465,23538,23539,23541,23543,23545,23547,23549,23551,23553],{"class":5467,"line":6265},[5465,23540,23419],{"class":5496},[5465,23542,5214],{"class":5489},[5465,23544,23446],{"class":5538},[5465,23546,5542],{"class":5489},[5465,23548,23451],{"class":5503},[5465,23550,5336],{"class":5489},[5465,23552,23456],{"class":5496},[5465,23554,5663],{"class":5489},[5465,23556,23557],{"class":5467,"line":6270},[5465,23558,23486],{"class":5489},[5465,23560,23561],{"class":5467,"line":6276},[5465,23562,10928],{"class":5489},[5465,23564,23565],{"class":5467,"line":6282},[5465,23566,10933],{"class":5489},[5465,23568,23569,23571,23573,23575],{"class":5467,"line":6288},[5465,23570,5693],{"class":5675},[5465,23572,5711],{"class":5496},[5465,23574,5336],{"class":5489},[5465,23576,14138],{"class":5496},[5465,23578,23579],{"class":5467,"line":6294},[5465,23580,5702],{"class":5489},[5465,23582,23583],{"class":5467,"line":6300},[5465,23584,5522],{"class":5489},[5465,23586,23587],{"class":5467,"line":6306},[5465,23588,5529],{"emptyLinePlaceholder":5528},[5465,23590,23591,23593,23595],{"class":5467,"line":6311},[5465,23592,5535],{"class":5478},[5465,23594,11266],{"class":5538},[5465,23596,11269],{"class":5489},[5465,23598,23599],{"class":5467,"line":6316},[5465,23600,23601],{"class":5471},"    \u002F\u002F JSON structured logging\n",[5465,23603,23604,23607,23609,23611,23613,23615,23617,23619,23621,23624,23626,23628,23630,23633,23635,23637,23639,23642],{"class":5467,"line":6322},[5465,23605,23606],{"class":5496},"    logger",[5465,23608,5576],{"class":5489},[5465,23610,16958],{"class":5496},[5465,23612,5214],{"class":5489},[5465,23614,22903],{"class":5538},[5465,23616,5542],{"class":5489},[5465,23618,16958],{"class":5496},[5465,23620,5214],{"class":5489},[5465,23622,23623],{"class":5538},"NewJSONHandler",[5465,23625,5542],{"class":5489},[5465,23627,16218],{"class":5496},[5465,23629,5214],{"class":5489},[5465,23631,23632],{"class":5496},"Stdout",[5465,23634,5920],{"class":5489},[5465,23636,16958],{"class":5482},[5465,23638,5214],{"class":5489},[5465,23640,23641],{"class":5482},"HandlerOptions",[5465,23643,5926],{"class":5489},[5465,23645,23646,23649,23651,23653,23655,23658],{"class":5467,"line":6328},[5465,23647,23648],{"class":5496},"        Level",[5465,23650,5807],{"class":5489},[5465,23652,16958],{"class":5496},[5465,23654,5214],{"class":5489},[5465,23656,23657],{"class":5496},"LevelInfo",[5465,23659,5636],{"class":5489},[5465,23661,23662],{"class":5467,"line":6334},[5465,23663,23664],{"class":5489},"    }))\n",[5465,23666,23667],{"class":5467,"line":6340},[5465,23668,5597],{"class":5489},[5465,23670,23671,23673,23675,23677,23679,23681],{"class":5467,"line":6346},[5465,23672,16148],{"class":5496},[5465,23674,5576],{"class":5489},[5465,23676,7555],{"class":5496},[5465,23678,5214],{"class":5489},[5465,23680,11368],{"class":5538},[5465,23682,5621],{"class":5489},[5465,23684,23685,23687,23689,23692,23694,23697,23699,23701],{"class":5467,"line":6352},[5465,23686,11375],{"class":5496},[5465,23688,5214],{"class":5489},[5465,23690,23691],{"class":5538},"UnaryInterceptor",[5465,23693,5542],{"class":5489},[5465,23695,23696],{"class":5538},"loggingInterceptor",[5465,23698,5542],{"class":5489},[5465,23700,17131],{"class":5496},[5465,23702,15274],{"class":5489},[5465,23704,23705],{"class":5467,"line":6358},[5465,23706,5669],{"class":5489},[5465,23708,23709],{"class":5467,"line":6364},[5465,23710,5597],{"class":5489},[5465,23712,23713],{"class":5467,"line":6369},[5465,23714,16177],{"class":5471},[5465,23716,23717],{"class":5467,"line":6374},[5465,23718,5522],{"class":5489},[5280,23720,23721],{},[5232,23722,23723],{},"Приклад виводу:",[5456,23725,23728],{"className":23726,"code":23727,"language":5579,"meta":5461,"style":5461},"language-json shiki shiki-themes light-plus dark-plus dark-plus","{\n  \"time\": \"2026-09-02T15:30:00Z\",\n  \"level\": \"INFO\",\n  \"msg\": \"gRPC request completed\",\n  \"method\": \"\u002Fuserservice.UserService\u002FCreateUser\",\n  \"duration\": \"15.3ms\"\n}\n",[5211,23729,23730,23734,23747,23759,23770,23782,23792],{"__ignoreMap":5461},[5465,23731,23732],{"class":5467,"line":5468},[5465,23733,5926],{"class":5489},[5465,23735,23736,23740,23742,23745],{"class":5467,"line":5475},[5465,23737,23739],{"class":23738},"sLwNe","  \"time\"",[5465,23741,5807],{"class":5489},[5465,23743,23744],{"class":5503},"\"2026-09-02T15:30:00Z\"",[5465,23746,5636],{"class":5489},[5465,23748,23749,23752,23754,23757],{"class":5467,"line":5493},[5465,23750,23751],{"class":23738},"  \"level\"",[5465,23753,5807],{"class":5489},[5465,23755,23756],{"class":5503},"\"INFO\"",[5465,23758,5636],{"class":5489},[5465,23760,23761,23764,23766,23768],{"class":5467,"line":5507},[5465,23762,23763],{"class":23738},"  \"msg\"",[5465,23765,5807],{"class":5489},[5465,23767,23512],{"class":5503},[5465,23769,5636],{"class":5489},[5465,23771,23772,23775,23777,23780],{"class":5467,"line":5519},[5465,23773,23774],{"class":23738},"  \"method\"",[5465,23776,5807],{"class":5489},[5465,23778,23779],{"class":5503},"\"\u002Fuserservice.UserService\u002FCreateUser\"",[5465,23781,5636],{"class":5489},[5465,23783,23784,23787,23789],{"class":5467,"line":5525},[5465,23785,23786],{"class":23738},"  \"duration\"",[5465,23788,5807],{"class":5489},[5465,23790,23791],{"class":5503},"\"15.3ms\"\n",[5465,23793,23794],{"class":5467,"line":5532},[5465,23795,5522],{"class":5489},[5280,23797,23798,6758,23800],{},[5232,23799,7435],{},[7379,23801,23804],{"href":23802,"rel":23803},"https:\u002F\u002Fpkg.go.dev\u002Flog\u002Fslog",[7383],"pkg.go.dev\u002Flog\u002Fslog",[5267,23806],{},[5270,23808,23810],{"id":23809},"підсумки-та-ключові-висновки","Підсумки та ключові висновки",[5190,23812,23813,23867],{},[5193,23814,23817],{"icon":23815,"title":23816},"i-lucide-check-circle","✅ Що ми опанували",[5198,23818,23819,23825,23834,23840,23846,23855,23861],{},[5201,23820,23821,23824],{},[5232,23822,23823],{},"Фундаментальні обмеження REST API"," у контексті міжсервісної комунікації та мотивацію переходу на RPC-підходи.",[5201,23826,23827,23830,23831,23833],{},[5232,23828,23829],{},"Мову опису контрактів Protocol Buffers"," та синтаксис файлів ",[5211,23832,5213],{}," для визначення повідомлень, enum-типів та сервісів.",[5201,23835,23836,23839],{},[5232,23837,23838],{},"Архітектуру транспортного рівня gRPC"," на базі HTTP\u002F2 та переваги мультиплексування, стиснення заголовків та потокових операцій.",[5201,23841,23842,23845],{},[5232,23843,23844],{},"Чотири типи RPC-викликів:"," Unary, Server Streaming, Client Streaming та Bidirectional Streaming із їх сценаріями застосування.",[5201,23847,23848,23851,23852,23854],{},[5232,23849,23850],{},"Повний цикл розробки gRPC-сервісу у Go:"," від компіляції ",[5211,23853,5213],{}," до реалізації серверних обробників та клієнтських stub-ів.",[5201,23856,23857,23860],{},[5232,23858,23859],{},"Систему інтерцепторів"," для додавання наскрізної функціональності (логування, автентифікація, метрики) без модифікації бізнес-логіки.",[5201,23862,23863,23866],{},[5232,23864,23865],{},"Підходи до балансування навантаження, mutual TLS та deadline propagation"," у продакшн-системах.",[5193,23868,23871],{"icon":23869,"title":23870},"i-lucide-graduation-cap","🎓 Практичні навички",[5198,23872,23873,23876,23879,23882,23885,23891],{},[5201,23874,23875],{},"Проєктування строго типізованих API контрактів у форматі Protocol Buffers із дотриманням прямої та зворотної сумісності.",[5201,23877,23878],{},"Реалізація високопродуктивних gRPC-сервісів у Go із підтримкою всіх типів RPC-викликів.",[5201,23880,23881],{},"Створення клієнтських бібліотек для різних платформ (Go, Java, C#, Python) через автоматичну кодогенерацію.",[5201,23883,23884],{},"Впровадження механізмів автентифікації JWT та mutual TLS для захисту міжсервісних комунікацій.",[5201,23886,23887,23888,23890],{},"Налагодження та тестування gRPC-сервісів через ",[5211,23889,11624],{},", модульні та інтеграційні тести.",[5201,23892,23893],{},"Прийняття обґрунтованих архітектурних рішень щодо вибору REST vs gRPC на основі вимог до продуктивності, читабельності та екосистеми клієнтів.",[9657,23895,23896,23940,24028],{},[9660,23897,23899,23902,23907,23918,23923,23934],{"icon":9662,"label":23898},"❓ Чи може gRPC повністю замінити REST у всіх сценаріях?",[5280,23900,23901],{},"Ні, gRPC не є універсальною заміною REST. Кожна технологія має свої сильні та слабкі сторони:",[5280,23903,23904],{},[5232,23905,23906],{},"REST залишається оптимальним для:",[5198,23908,23909,23912,23915],{},[5201,23910,23911],{},"Публічних API, що споживаються вебклієнтами у браузері.",[5201,23913,23914],{},"Простих CRUD-операцій, де читабельність та швидкість розробки важливіші за продуктивність.",[5201,23916,23917],{},"Інтеграцій із системами, що не підтримують HTTP\u002F2 або бінарні протоколи.",[5280,23919,23920],{},[5232,23921,23922],{},"gRPC перевершує REST у:",[5198,23924,23925,23928,23931],{},[5201,23926,23927],{},"Міжсервісній комунікації у мікросервісних архітектурах.",[5201,23929,23930],{},"Системах із жорсткими вимогами до латентності та пропускної здатності.",[5201,23932,23933],{},"Потокових операціях (завантаження файлів, real-time дані).",[5280,23935,23936,23937,23939],{},"Багато компаній використовують ",[5232,23938,19987],{},": gRPC для внутрішньої комунікації між бекенд-сервісами та REST (або GraphQL) для зовнішнього API, що обслуговує клієнтські застосунки.",[9660,23941,23943,23954,23959,23982,23987,24005,24010],{"icon":9662,"label":23942},"❓ Як забезпечити зворотну сумісність при зміні .proto файлів?",[5280,23944,23945,23946,23949,23950,23953],{},"Protocol Buffers забезпечують зворотну сумісність (",[5284,23947,23948],{},"backward compatibility",") та пряму сумісність (",[5284,23951,23952],{},"forward compatibility",") за умови дотримання наступних правил:",[5280,23955,23956],{},[5232,23957,23958],{},"Безпечні зміни (не порушують сумісність):",[5198,23960,23961,23964,23967,23970],{},[5201,23962,23963],{},"Додавання нових полів із новими field numbers.",[5201,23965,23966],{},"Додавання нових значень до enum (крім першого значення з номером 0).",[5201,23968,23969],{},"Додавання нових RPC-методів до сервісу.",[5201,23971,23972,23973,23975,23976,5336,23978,23975,23980,6446],{},"Зміна типу поля на сумісний тип (наприклад, ",[5211,23974,6886],{}," → ",[5211,23977,6761],{},[5211,23979,6764],{},[5211,23981,7006],{},[5280,23983,23984],{},[5232,23985,23986],{},"Небезпечні зміни (порушують сумісність):",[5198,23988,23989,23992,23999,24002],{},[5201,23990,23991],{},"Зміна або видалення існуючих field numbers.",[5201,23993,23994,23995,23975,23997,6446],{},"Зміна типу даних поля на несумісний (наприклад, ",[5211,23996,6764],{},[5211,23998,6886],{},[5201,24000,24001],{},"Перейменування полів (це безпечно на рівні бінарного протоколу, але порушує згенерований код).",[5201,24003,24004],{},"Зміна назви package або сервісу.",[5280,24006,24007],{},[5232,24008,24009],{},"Best practices:",[5198,24011,24012,24018,24025],{},[5201,24013,24014,24015,5214],{},"Ніколи не видаляйте field numbers — позначайте їх як ",[5211,24016,24017],{},"reserved",[5201,24019,24020,24021,24024],{},"Використовуйте коментарі ",[5211,24022,24023],{},"deprecated"," для застарілих полів.",[5201,24026,24027],{},"Додавайте нові поля замість модифікації існуючих.",[9660,24029,24031,24034,24039,24076,24079,24084,24121],{"icon":9662,"label":24030},"❓ Як організувати версіювання gRPC API при breaking changes?",[5280,24032,24033],{},"Якщо необхідно внести breaking changes, використовуйте одну з наступних стратегій версіювання:",[5280,24035,24036],{},[5232,24037,24038],{},"1. Версіювання через package name:",[5456,24040,24042],{"className":6471,"code":24041,"language":6473,"meta":5461,"style":5461},"syntax = \"proto3\";\n\npackage userservice.v1;  \u002F\u002F Стара версія\n\u002F\u002F ... визначення сервісів\n\npackage userservice.v2;  \u002F\u002F Нова версія із breaking changes\n\u002F\u002F ... нові визначення сервісів\n",[5211,24043,24044,24048,24052,24057,24062,24066,24071],{"__ignoreMap":5461},[5465,24045,24046],{"class":5467,"line":5468},[5465,24047,6485],{},[5465,24049,24050],{"class":5467,"line":5475},[5465,24051,5529],{"emptyLinePlaceholder":5528},[5465,24053,24054],{"class":5467,"line":5493},[5465,24055,24056],{},"package userservice.v1;  \u002F\u002F Стара версія\n",[5465,24058,24059],{"class":5467,"line":5507},[5465,24060,24061],{},"\u002F\u002F ... визначення сервісів\n",[5465,24063,24064],{"class":5467,"line":5519},[5465,24065,5529],{"emptyLinePlaceholder":5528},[5465,24067,24068],{"class":5467,"line":5525},[5465,24069,24070],{},"package userservice.v2;  \u002F\u002F Нова версія із breaking changes\n",[5465,24072,24073],{"class":5467,"line":5532},[5465,24074,24075],{},"\u002F\u002F ... нові визначення сервісів\n",[5280,24077,24078],{},"Обидві версії можуть співіснувати на одному сервері, клієнти поступово мігрують на v2.",[5280,24080,24081],{},[5232,24082,24083],{},"2. Версіювання через окремі сервіси:",[5456,24085,24087],{"className":6471,"code":24086,"language":6473,"meta":5461,"style":5461},"service UserServiceV1 {\n    rpc CreateUser(CreateUserRequestV1) returns (CreateUserResponseV1);\n}\n\nservice UserServiceV2 {\n    rpc CreateUser(CreateUserRequestV2) returns (CreateUserResponseV2);\n}\n",[5211,24088,24089,24094,24099,24103,24107,24112,24117],{"__ignoreMap":5461},[5465,24090,24091],{"class":5467,"line":5468},[5465,24092,24093],{},"service UserServiceV1 {\n",[5465,24095,24096],{"class":5467,"line":5475},[5465,24097,24098],{},"    rpc CreateUser(CreateUserRequestV1) returns (CreateUserResponseV1);\n",[5465,24100,24101],{"class":5467,"line":5493},[5465,24102,5522],{},[5465,24104,24105],{"class":5467,"line":5507},[5465,24106,5529],{"emptyLinePlaceholder":5528},[5465,24108,24109],{"class":5467,"line":5519},[5465,24110,24111],{},"service UserServiceV2 {\n",[5465,24113,24114],{"class":5467,"line":5525},[5465,24115,24116],{},"    rpc CreateUser(CreateUserRequestV2) returns (CreateUserResponseV2);\n",[5465,24118,24119],{"class":5467,"line":5532},[5465,24120,5522],{},[5280,24122,24123,24126],{},[5232,24124,24125],{},"3. Використання feature flags та поступова міграція:","\nДодайте нове поле-прапорець, що вмикає нову логіку, зберігаючи стару логіку для старих клієнтів. Після міграції всіх клієнтів видаліть стару логіку.",[5267,24128],{},[5270,24130,24132],{"id":24131},"рекомендована-література-та-ресурси","Рекомендована література та ресурси",[5190,24134,24135,24176,24214],{},[5193,24136,24138],{"icon":15,"title":24137},"📚 Офіційна документація",[5198,24139,24140,24149,24158,24167],{},[5201,24141,24142,6758,24145],{},[5232,24143,24144],{},"gRPC Official Documentation:",[7379,24146,24147],{"href":24147,"rel":24148},"https:\u002F\u002Fgrpc.io\u002Fdocs\u002F",[7383],[5201,24150,24151,6758,24154],{},[5232,24152,24153],{},"Protocol Buffers Language Guide:",[7379,24155,24156],{"href":24156,"rel":24157},"https:\u002F\u002Fprotobuf.dev\u002Fprogramming-guides\u002Fproto3\u002F",[7383],[5201,24159,24160,6758,24163],{},[5232,24161,24162],{},"gRPC Go Quick Start:",[7379,24164,24165],{"href":24165,"rel":24166},"https:\u002F\u002Fgrpc.io\u002Fdocs\u002Flanguages\u002Fgo\u002Fquickstart\u002F",[7383],[5201,24168,24169,6758,24172],{},[5232,24170,24171],{},"HTTP\u002F2 RFC 7540:",[7379,24173,24174],{"href":24174,"rel":24175},"https:\u002F\u002Fdatatracker.ietf.org\u002Fdoc\u002Fhtml\u002Frfc7540",[7383],[5193,24177,24179],{"icon":3072,"title":24178},"🛠️ Інструменти та бібліотеки",[5198,24180,24181,24189,24197,24205],{},[5201,24182,24183,6758,24186],{},[5232,24184,24185],{},"grpcurl — CLI tool for gRPC testing:",[7379,24187,17303],{"href":17303,"rel":24188},[7383],[5201,24190,24191,6758,24194],{},[5232,24192,24193],{},"BloomRPC — GUI client for gRPC:",[7379,24195,19275],{"href":19275,"rel":24196},[7383],[5201,24198,24199,6758,24202],{},[5232,24200,24201],{},"go-grpc-middleware — Collection of interceptors:",[7379,24203,17149],{"href":17149,"rel":24204},[7383],[5201,24206,24207,6758,24210],{},[5232,24208,24209],{},"buf — Modern Protobuf toolchain:",[7379,24211,24212],{"href":24212,"rel":24213},"https:\u002F\u002Fbuf.build\u002F",[7383],[5193,24215,24218],{"icon":24216,"title":24217},"i-lucide-library","📖 Додаткові матеріали",[5198,24219,24220,24226,24232],{},[5201,24221,24222,24225],{},[5232,24223,24224],{},"\"Building Microservices\" by Sam Newman"," — розділ про міжсервісну комунікацію та порівняння REST vs RPC.",[5201,24227,24228,24231],{},[5232,24229,24230],{},"\"gRPC: Up and Running\" by Kasun Indrasiri & Danesh Kuruppu"," — поглиблене вивчення gRPC із прикладами на Go, Java та Python.",[5201,24233,24234,24237],{},[5232,24235,24236],{},"Google Cloud Architecture Center:"," Design patterns для gRPC у хмарних середовищах.",[24239,24240,24241],"style",{},"html pre.shiki code .spJ8K, html code.shiki .spJ8K{--shiki-light:#008000;--shiki-default:#6A9955;--shiki-dark:#6A9955}html pre.shiki code .su1O8, html code.shiki .su1O8{--shiki-light:#0000FF;--shiki-default:#569CD6;--shiki-dark:#569CD6}html pre.shiki code .sN1BT, html code.shiki .sN1BT{--shiki-light:#267F99;--shiki-default:#4EC9B0;--shiki-dark:#4EC9B0}html pre.shiki code .sHH4Y, html code.shiki .sHH4Y{--shiki-light:#000000;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}html pre.shiki code .siwwj, html code.shiki .siwwj{--shiki-light:#001080;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .sbdoH, html code.shiki .sbdoH{--shiki-light:#A31515;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .s8Opu, html code.shiki .s8Opu{--shiki-light:#795E26;--shiki-default:#DCDCAA;--shiki-dark:#DCDCAA}html pre.shiki code .s8xlr, html code.shiki .s8xlr{--shiki-light:#AF00DB;--shiki-default:#C586C0;--shiki-dark:#C586C0}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJj4R, html code.shiki .sJj4R{--shiki-light:#098658;--shiki-default:#B5CEA8;--shiki-dark:#B5CEA8}html pre.shiki code .sjcCO, html code.shiki .sjcCO{--shiki-light:#EE0000;--shiki-default:#D7BA7D;--shiki-dark:#D7BA7D}html pre.shiki code .sKtos, html code.shiki .sKtos{--shiki-light:#800000;--shiki-default:#569CD6;--shiki-dark:#569CD6}html pre.shiki code .s-QsJ, html code.shiki .s-QsJ{--shiki-light:#0070C1;--shiki-default:#4FC1FF;--shiki-dark:#4FC1FF}html pre.shiki code .su9tN, html code.shiki .su9tN{--shiki-light:#0000FF;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .sLwNe, html code.shiki .sLwNe{--shiki-light:#0451A5;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}",{"title":5461,"searchDepth":5475,"depth":5475,"links":24243},[24244,24249,24256,24261,24265,24268,24272,24278,24284,24288,24294,24295,24296],{"id":5272,"depth":5475,"text":5273,"children":24245},[24246,24247,24248],{"id":5277,"depth":5493,"text":5278},{"id":5435,"depth":5493,"text":5436},{"id":5990,"depth":5493,"text":5991},{"id":6382,"depth":5475,"text":6383,"children":24250},[24251,24252,24253,24254,24255],{"id":6386,"depth":5493,"text":6387},{"id":6449,"depth":5493,"text":6450},{"id":6849,"depth":5493,"text":6850},{"id":7067,"depth":5493,"text":7068},{"id":7343,"depth":5493,"text":7344},{"id":8093,"depth":5475,"text":8094,"children":24257},[24258,24259,24260],{"id":8097,"depth":5493,"text":8098},{"id":8225,"depth":5493,"text":8226},{"id":8563,"depth":5493,"text":8564},{"id":8629,"depth":5475,"text":8630,"children":24262},[24263,24264],{"id":8633,"depth":5493,"text":8634},{"id":9076,"depth":5493,"text":9077},{"id":9767,"depth":5475,"text":9768,"children":24266},[24267],{"id":9771,"depth":5493,"text":9772},{"id":11642,"depth":5475,"text":11643,"children":24269},[24270,24271],{"id":11646,"depth":5493,"text":11647},{"id":12718,"depth":5493,"text":12719},{"id":13642,"depth":5475,"text":13643,"children":24273},[24274,24275,24276,24277],{"id":13646,"depth":5493,"text":13647},{"id":13699,"depth":5493,"text":13700},{"id":14225,"depth":5493,"text":14226},{"id":16525,"depth":5493,"text":16526},{"id":17634,"depth":5475,"text":17635,"children":24279},[24280,24281,24282,24283],{"id":17638,"depth":5493,"text":17639},{"id":18228,"depth":5493,"text":18229},{"id":19019,"depth":5493,"text":19020},{"id":19249,"depth":5493,"text":19250},{"id":19689,"depth":5475,"text":19690,"children":24285},[24286,24287],{"id":19693,"depth":5493,"text":19694},{"id":19899,"depth":5493,"text":19900},{"id":19993,"depth":5475,"text":19994,"children":24289},[24290,24291,24292,24293],{"id":19997,"depth":5493,"text":19998},{"id":20172,"depth":5493,"text":20173},{"id":20484,"depth":5493,"text":20485},{"id":21194,"depth":5493,"text":21195},{"id":22042,"depth":5475,"text":22043},{"id":23809,"depth":5475,"text":23810},{"id":24131,"depth":5475,"text":24132},"Концепція віддаленого виклику процедур (RPC). Формат бінарної серіалізації Protocol Buffers. Транспорт HTTP\u002F2 у gRPC. Типи викликів: Unary, Server Streaming, Client Streaming, Bidirectional Streaming. Реалізація сервісів, інтерцептори та генерація клієнтських стабів.",null,{},{"title":4045,"description":24297},"rJ-Nur2JQ7DBadmcj1AM9DCrYXECdQP0Hz_NQ_NYmQA",[24303,24305],{"title":4041,"path":4042,"stem":4043,"description":24304,"children":-1},"Технології доставки даних у реальному часі: Server-Sent Events (SSE) та протокол WebSocket (RFC 6455). Встановлення з'єднання через HTTP Upgrade, фреймінг повідомлень, патерн Hub та броадкаст у Go.",{"title":4049,"path":4050,"stem":4051,"description":24306,"children":-1},"Парадигма асинхронного обміну повідомленнями. Протокол AMQP 0-9-1 та брокер RabbitMQ. Producers, Exchanges, Queues, Bindings. Протокол MQTT для IoT та мобільних пристроїв. Рівні якості обслуговування QoS, топіки та клієнтська інтеграція у Go.",1791986749337]