[{"data":1,"prerenderedAt":1011},["ShallowReactive",2],{"content-\u002F05-web\u002F04-todo-project":3},{"id":4,"title":5,"body":6,"description":45,"difficulty":992,"extension":993,"meta":994,"module":995,"navigation":996,"next":997,"order":90,"path":998,"prev":999,"seo":1000,"slug":1001,"stem":1002,"tags":1003,"__hash__":1010},"content\u002F05-web\u002F04-todo-project\u002Findex.md","Итоговый проект ToDo",{"type":7,"value":8,"toc":976},"minimark",[9,14,18,21,25,30,33,39,118,122,229,233,241,244,264,271,317,323,342,344,352,357,449,460,462,470,474,514,520,538,540,548,551,577,582,586,602,604,612,618,622,638,640,648,652,671,673,677,727,731,748,750,754,760,764,775,779,784,823,828,843,848,869,874,888,892,899,901,905,916,921,947,952,972],[10,11,13],"h2",{"id":12},"описание","Описание",[15,16,17],"p",{},"Написать REST API для управления списком задач на Go с использованием фреймворка Echo. Проект объединяет всё что мы изучили: типы данных, структуры, интерфейсы, конкурентность, контекст и работу с HTTP.",[19,20],"hr",{},[10,22,24],{"id":23},"требования","Требования",[26,27,29],"h3",{"id":28},"что-нужно-реализовать","Что нужно реализовать",[15,31,32],{},"Приложение должно хранить задачи в памяти (обычный map) и предоставлять REST API для работы с ними.",[15,34,35],{},[36,37,38],"strong",{},"Модель задачи:",[40,41,46],"pre",{"className":42,"code":43,"language":44,"meta":45,"style":45},"language-json shiki shiki-themes github-dark","{\n    \"id\": 1,\n    \"title\": \"Купить молоко\",\n    \"done\": false,\n    \"created_at\": \"2024-01-15T10:30:00Z\"\n}\n","json","",[47,48,49,58,74,88,101,112],"code",{"__ignoreMap":45},[50,51,54],"span",{"class":52,"line":53},"line",1,[50,55,57],{"class":56},"s95oV","{\n",[50,59,61,65,68,71],{"class":52,"line":60},2,[50,62,64],{"class":63},"sDLfK","    \"id\"",[50,66,67],{"class":56},": ",[50,69,70],{"class":63},"1",[50,72,73],{"class":56},",\n",[50,75,77,80,82,86],{"class":52,"line":76},3,[50,78,79],{"class":63},"    \"title\"",[50,81,67],{"class":56},[50,83,85],{"class":84},"sU2Wk","\"Купить молоко\"",[50,87,73],{"class":56},[50,89,91,94,96,99],{"class":52,"line":90},4,[50,92,93],{"class":63},"    \"done\"",[50,95,67],{"class":56},[50,97,98],{"class":63},"false",[50,100,73],{"class":56},[50,102,104,107,109],{"class":52,"line":103},5,[50,105,106],{"class":63},"    \"created_at\"",[50,108,67],{"class":56},[50,110,111],{"class":84},"\"2024-01-15T10:30:00Z\"\n",[50,113,115],{"class":52,"line":114},6,[50,116,117],{"class":56},"}\n",[26,119,121],{"id":120},"эндпоинты","Эндпоинты",[123,124,125,140],"table",{},[126,127,128],"thead",{},[129,130,131,135,138],"tr",{},[132,133,134],"th",{},"Метод",[132,136,137],{},"URL",[132,139,13],{},[141,142,143,159,173,187,201,215],"tbody",{},[129,144,145,151,156],{},[146,147,148],"td",{},[47,149,150],{},"GET",[146,152,153],{},[47,154,155],{},"\u002Fapi\u002Ftodos",[146,157,158],{},"Получить все задачи",[129,160,161,165,170],{},[146,162,163],{},[47,164,150],{},[146,166,167],{},[47,168,169],{},"\u002Fapi\u002Ftodos\u002F:id",[146,171,172],{},"Получить задачу по ID",[129,174,175,180,184],{},[146,176,177],{},[47,178,179],{},"POST",[146,181,182],{},[47,183,155],{},[146,185,186],{},"Создать задачу",[129,188,189,194,198],{},[146,190,191],{},[47,192,193],{},"PUT",[146,195,196],{},[47,197,169],{},[146,199,200],{},"Обновить задачу",[129,202,203,208,212],{},[146,204,205],{},[47,206,207],{},"DELETE",[146,209,210],{},[47,211,169],{},[146,213,214],{},"Удалить задачу",[129,216,217,221,226],{},[146,218,219],{},[47,220,150],{},[146,222,223],{},[47,224,225],{},"\u002Fhealth",[146,227,228],{},"Проверка что сервер жив",[26,230,232],{"id":231},"детали-каждого-эндпоинта","Детали каждого эндпоинта",[15,234,235,240],{},[36,236,237],{},[47,238,239],{},"POST \u002Fapi\u002Ftodos"," — создать задачу",[15,242,243],{},"Тело запроса:",[40,245,247],{"className":42,"code":246,"language":44,"meta":45,"style":45},"{ \"title\": \"Купить молоко\" }\n",[47,248,249],{"__ignoreMap":45},[50,250,251,254,257,259,261],{"class":52,"line":53},[50,252,253],{"class":56},"{ ",[50,255,256],{"class":63},"\"title\"",[50,258,67],{"class":56},[50,260,85],{"class":84},[50,262,263],{"class":56}," }\n",[15,265,266,267,270],{},"Успешный ответ ",[47,268,269],{},"201 Created",":",[40,272,274],{"className":42,"code":273,"language":44,"meta":45,"style":45},"{ \"id\": 1, \"title\": \"Купить молоко\", \"done\": false, \"created_at\": \"...\" }\n",[47,275,276],{"__ignoreMap":45},[50,277,278,280,283,285,287,290,292,294,296,298,301,303,305,307,310,312,315],{"class":52,"line":53},[50,279,253],{"class":56},[50,281,282],{"class":63},"\"id\"",[50,284,67],{"class":56},[50,286,70],{"class":63},[50,288,289],{"class":56},", ",[50,291,256],{"class":63},[50,293,67],{"class":56},[50,295,85],{"class":84},[50,297,289],{"class":56},[50,299,300],{"class":63},"\"done\"",[50,302,67],{"class":56},[50,304,98],{"class":63},[50,306,289],{"class":56},[50,308,309],{"class":63},"\"created_at\"",[50,311,67],{"class":56},[50,313,314],{"class":84},"\"...\"",[50,316,263],{"class":56},[15,318,319,320,270],{},"Ошибка если title пустой ",[47,321,322],{},"400 Bad Request",[40,324,326],{"className":42,"code":325,"language":44,"meta":45,"style":45},"{ \"error\": \"title is required\" }\n",[47,327,328],{"__ignoreMap":45},[50,329,330,332,335,337,340],{"class":52,"line":53},[50,331,253],{"class":56},[50,333,334],{"class":63},"\"error\"",[50,336,67],{"class":56},[50,338,339],{"class":84},"\"title is required\"",[50,341,263],{"class":56},[19,343],{},[15,345,346,351],{},[36,347,348],{},[47,349,350],{},"GET \u002Fapi\u002Ftodos"," — получить все задачи",[15,353,266,354,270],{},[47,355,356],{},"200 OK",[40,358,360],{"className":42,"code":359,"language":44,"meta":45,"style":45},"[\n    { \"id\": 1, \"title\": \"Купить молоко\", \"done\": false, \"created_at\": \"...\" },\n    { \"id\": 2, \"title\": \"Позвонить маме\", \"done\": true, \"created_at\": \"...\" }\n]\n",[47,361,362,367,405,444],{"__ignoreMap":45},[50,363,364],{"class":52,"line":53},[50,365,366],{"class":56},"[\n",[50,368,369,372,374,376,378,380,382,384,386,388,390,392,394,396,398,400,402],{"class":52,"line":60},[50,370,371],{"class":56},"    { ",[50,373,282],{"class":63},[50,375,67],{"class":56},[50,377,70],{"class":63},[50,379,289],{"class":56},[50,381,256],{"class":63},[50,383,67],{"class":56},[50,385,85],{"class":84},[50,387,289],{"class":56},[50,389,300],{"class":63},[50,391,67],{"class":56},[50,393,98],{"class":63},[50,395,289],{"class":56},[50,397,309],{"class":63},[50,399,67],{"class":56},[50,401,314],{"class":84},[50,403,404],{"class":56}," },\n",[50,406,407,409,411,413,416,418,420,422,425,427,429,431,434,436,438,440,442],{"class":52,"line":76},[50,408,371],{"class":56},[50,410,282],{"class":63},[50,412,67],{"class":56},[50,414,415],{"class":63},"2",[50,417,289],{"class":56},[50,419,256],{"class":63},[50,421,67],{"class":56},[50,423,424],{"class":84},"\"Позвонить маме\"",[50,426,289],{"class":56},[50,428,300],{"class":63},[50,430,67],{"class":56},[50,432,433],{"class":63},"true",[50,435,289],{"class":56},[50,437,309],{"class":63},[50,439,67],{"class":56},[50,441,314],{"class":84},[50,443,263],{"class":56},[50,445,446],{"class":52,"line":90},[50,447,448],{"class":56},"]\n",[15,450,451,452,455,456,459],{},"Если задач нет — вернуть пустой массив ",[47,453,454],{},"[]",", не ",[47,457,458],{},"null",".",[19,461],{},[15,463,464,469],{},[36,465,466],{},[47,467,468],{},"GET \u002Fapi\u002Ftodos\u002F:id"," — получить задачу по ID",[15,471,266,472,270],{},[47,473,356],{},[40,475,476],{"className":42,"code":273,"language":44,"meta":45,"style":45},[47,477,478],{"__ignoreMap":45},[50,479,480,482,484,486,488,490,492,494,496,498,500,502,504,506,508,510,512],{"class":52,"line":53},[50,481,253],{"class":56},[50,483,282],{"class":63},[50,485,67],{"class":56},[50,487,70],{"class":63},[50,489,289],{"class":56},[50,491,256],{"class":63},[50,493,67],{"class":56},[50,495,85],{"class":84},[50,497,289],{"class":56},[50,499,300],{"class":63},[50,501,67],{"class":56},[50,503,98],{"class":63},[50,505,289],{"class":56},[50,507,309],{"class":63},[50,509,67],{"class":56},[50,511,314],{"class":84},[50,513,263],{"class":56},[15,515,516,517,270],{},"Ошибка если не найдена ",[47,518,519],{},"404 Not Found",[40,521,523],{"className":42,"code":522,"language":44,"meta":45,"style":45},"{ \"error\": \"todo not found\" }\n",[47,524,525],{"__ignoreMap":45},[50,526,527,529,531,533,536],{"class":52,"line":53},[50,528,253],{"class":56},[50,530,334],{"class":63},[50,532,67],{"class":56},[50,534,535],{"class":84},"\"todo not found\"",[50,537,263],{"class":56},[19,539],{},[15,541,542,547],{},[36,543,544],{},[47,545,546],{},"PUT \u002Fapi\u002Ftodos\u002F:id"," — обновить задачу",[15,549,550],{},"Тело запроса (оба поля опциональны):",[40,552,554],{"className":42,"code":553,"language":44,"meta":45,"style":45},"{ \"title\": \"Купить хлеб\", \"done\": true }\n",[47,555,556],{"__ignoreMap":45},[50,557,558,560,562,564,567,569,571,573,575],{"class":52,"line":53},[50,559,253],{"class":56},[50,561,256],{"class":63},[50,563,67],{"class":56},[50,565,566],{"class":84},"\"Купить хлеб\"",[50,568,289],{"class":56},[50,570,300],{"class":63},[50,572,67],{"class":56},[50,574,433],{"class":63},[50,576,263],{"class":56},[15,578,266,579,581],{},[47,580,356],{}," — возвращает обновлённую задачу.",[15,583,516,584,270],{},[47,585,519],{},[40,587,588],{"className":42,"code":522,"language":44,"meta":45,"style":45},[47,589,590],{"__ignoreMap":45},[50,591,592,594,596,598,600],{"class":52,"line":53},[50,593,253],{"class":56},[50,595,334],{"class":63},[50,597,67],{"class":56},[50,599,535],{"class":84},[50,601,263],{"class":56},[19,603],{},[15,605,606,611],{},[36,607,608],{},[47,609,610],{},"DELETE \u002Fapi\u002Ftodos\u002F:id"," — удалить задачу",[15,613,266,614,617],{},[47,615,616],{},"204 No Content"," — пустое тело.",[15,619,516,620,270],{},[47,621,519],{},[40,623,624],{"className":42,"code":522,"language":44,"meta":45,"style":45},[47,625,626],{"__ignoreMap":45},[50,627,628,630,632,634,636],{"class":52,"line":53},[50,629,253],{"class":56},[50,631,334],{"class":63},[50,633,67],{"class":56},[50,635,535],{"class":84},[50,637,263],{"class":56},[19,639],{},[15,641,642,647],{},[36,643,644],{},[47,645,646],{},"GET \u002Fhealth"," — health check",[15,649,266,650,270],{},[47,651,356],{},[40,653,655],{"className":42,"code":654,"language":44,"meta":45,"style":45},"{ \"status\": \"ok\" }\n",[47,656,657],{"__ignoreMap":45},[50,658,659,661,664,666,669],{"class":52,"line":53},[50,660,253],{"class":56},[50,662,663],{"class":63},"\"status\"",[50,665,67],{"class":56},[50,667,668],{"class":84},"\"ok\"",[50,670,263],{"class":56},[19,672],{},[26,674,676],{"id":675},"технические-требования","Технические требования",[678,679,680,687,694,700,703,709,716],"ul",{},[681,682,683,684],"li",{},"Фреймворк: ",[36,685,686],{},"Echo v4",[681,688,689,690,693],{},"Хранилище: ",[36,691,692],{},"map в памяти"," — никаких баз данных",[681,695,696,697],{},"Конкурентный доступ к map защищён через ",[36,698,699],{},"sync.RWMutex",[681,701,702],{},"ID задач генерируются автоматически и увеличиваются последовательно",[681,704,705,706],{},"Сервер запускается на порту ",[36,707,708],{},":8080",[681,710,711,712,715],{},"Сервер поддерживает ",[36,713,714],{},"Graceful Shutdown"," (SIGINT\u002FSIGTERM)",[681,717,718,719,722,723,726],{},"Middleware: ",[36,720,721],{},"Logger"," и ",[36,724,725],{},"Recover"," из echo\u002Fmiddleware",[26,728,730],{"id":729},"что-не-нужно","Что не нужно",[678,732,733,736,739,742],{},[681,734,735],{},"База данных",[681,737,738],{},"Аутентификация",[681,740,741],{},"Пагинация",[681,743,744,745],{},"Сложная архитектура — можно всё в одном файле ",[47,746,747],{},"main.go",[19,749],{},[10,751,753],{"id":752},"как-тестировать-через-postman","Как тестировать через Postman",[15,755,756,759],{},[36,757,758],{},"Postman"," — инструмент для тестирования API. Позволяет отправлять HTTP-запросы и смотреть ответы без написания кода.",[26,761,763],{"id":762},"установка","Установка",[15,765,766,767,774],{},"Скачать с ",[768,769,773],"a",{"href":770,"rel":771},"https:\u002F\u002Fwww.postman.com\u002Fdownloads\u002F",[772],"nofollow","postman.com"," — есть версия для Windows, Mac и Linux. Также есть веб-версия без установки.",[26,776,778],{"id":777},"базовое-использование","Базовое использование",[15,780,781],{},[36,782,783],{},"Создать задачу:",[785,786,787,792,798,811,817],"ol",{},[681,788,789,790],{},"Выбрать метод ",[47,791,179],{},[681,793,794,795],{},"URL: ",[47,796,797],{},"http:\u002F\u002Flocalhost:8080\u002Fapi\u002Ftodos",[681,799,800,801,804,805,804,808],{},"Вкладка ",[47,802,803],{},"Body"," → ",[47,806,807],{},"raw",[47,809,810],{},"JSON",[681,812,813,814],{},"Вставить тело: ",[47,815,816],{},"{\"title\": \"Купить молоко\"}",[681,818,819,820],{},"Нажать ",[47,821,822],{},"Send",[15,824,825],{},[36,826,827],{},"Получить все задачи:",[785,829,830,835,839],{},[681,831,832,833],{},"Метод ",[47,834,150],{},[681,836,794,837],{},[47,838,797],{},[681,840,819,841],{},[47,842,822],{},[15,844,845],{},[36,846,847],{},"Обновить задачу:",[785,849,850,854,859,865],{},[681,851,832,852],{},[47,853,193],{},[681,855,794,856],{},[47,857,858],{},"http:\u002F\u002Flocalhost:8080\u002Fapi\u002Ftodos\u002F1",[681,860,861,862],{},"Body → raw → JSON: ",[47,863,864],{},"{\"done\": true}",[681,866,819,867],{},[47,868,822],{},[15,870,871],{},[36,872,873],{},"Удалить задачу:",[785,875,876,880,884],{},[681,877,832,878],{},[47,879,207],{},[681,881,794,882],{},[47,883,858],{},[681,885,819,886],{},[47,887,822],{},[26,889,891],{"id":890},"коллекция-запросов","Коллекция запросов",[15,893,894,895,898],{},"В Postman можно сохранить все запросы в коллекцию — кнопка ",[47,896,897],{},"Save"," после каждого запроса. Это удобно чтобы не вводить URL каждый раз заново.",[19,900],{},[10,902,904],{"id":903},"фронтенд-для-проверки","Фронтенд для проверки",[15,906,907,908,911,912,915],{},"Сохрани этот файл как ",[47,909,910],{},"index.html"," рядом с проектом и открой в браузере. Он автоматически подключается к серверу на ",[47,913,914],{},"localhost:8080",":Вот что у нас есть для ученика:",[15,917,918],{},[36,919,920],{},"Фронтенд умеет:",[678,922,923,928,931,936,941,944],{},[681,924,925,926],{},"Показывать статус сервера (online\u002Foffline) через ",[47,927,225],{},[681,929,930],{},"Создавать задачи (Enter или кнопка)",[681,932,933,934],{},"Отмечать выполненными через ",[47,935,546],{},[681,937,938,939],{},"Удалять через ",[47,940,610],{},[681,942,943],{},"Фильтровать: Все \u002F Активные \u002F Выполненные",[681,945,946],{},"Показывать лог всех запросов к API с методом, статусом и временем ответа",[15,948,949],{},[36,950,951],{},"Инструкция для ученика:",[785,953,954,957,963,969],{},[681,955,956],{},"Написать сервер согласно требованиям",[681,958,959,960],{},"Запустить: ",[47,961,962],{},"go run main.go",[681,964,965,966,968],{},"Открыть ",[47,967,910],{}," в браузере",[681,970,971],{},"Параллельно смотреть в Postman детали запросов\u002Fответов",[973,974,975],"style",{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}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);}",{"title":45,"searchDepth":60,"depth":60,"links":977},[978,979,986,991],{"id":12,"depth":60,"text":13},{"id":23,"depth":60,"text":24,"children":980},[981,982,983,984,985],{"id":28,"depth":76,"text":29},{"id":120,"depth":76,"text":121},{"id":231,"depth":76,"text":232},{"id":675,"depth":76,"text":676},{"id":729,"depth":76,"text":730},{"id":752,"depth":60,"text":753,"children":987},[988,989,990],{"id":762,"depth":76,"text":763},{"id":777,"depth":76,"text":778},{"id":890,"depth":76,"text":891},{"id":903,"depth":60,"text":904},"intermediate","md",{},"web",true,"clean-architecture","\u002F05-web\u002F04-todo-project","rest-api",{"title":5,"description":45},"todo-project","05-web\u002F04-todo-project\u002Findex",[1004,1005,1006,1007,1008,1009],"проект","ToDo","CRUD","слои","практика","собеседование","gtf9vPGXA3Ljr0xuLn3CIwVB0pcKaCTn8FtMaYpJT7k",1776289836218]