{"id":411863,"date":"2024-06-29T22:13:05","date_gmt":"2024-06-29T22:13:05","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=411863"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-06-29T22:13:05","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=411863","title":{"rendered":"<span>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 Ariadne \u0438 \u0435\u0433\u043e \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f c FastAPI \u0438 Starlette<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0421 \u043d\u0435\u0434\u0430\u0432\u043d\u0435\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 Starlette \u043f\u0440\u0435\u043a\u0440\u0430\u0449\u0435\u043d\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 GraphQL. \u0422\u0430\u043a \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u0432\u044b, \u043a\u0430\u043a \u0438 \u043c\u044b, \u0437\u0430\u043d\u0438\u043c\u0430\u043b\u0438\u0441\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043d\u0430 FastAPI, \u0442\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 Starlette \u0432\u0430\u0441 \u043d\u0435\u043f\u0440\u0438\u044f\u0442\u043d\u043e \u0443\u0434\u0438\u0432\u0438\u043b\u0438.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/512\/ff7\/216\/512ff721679ef4539f7d80d2dffe1061.png\" width=\"1022\" height=\"387\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/512\/ff7\/216\/512ff721679ef4539f7d80d2dffe1061.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u041f\u0440\u0438\u0447\u0438\u043d\u044b, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u044d\u0442\u043e \u0441\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c, \u043d\u0435 \u0441\u0442\u043e\u043b\u044c \u0432\u0430\u0436\u043d\u044b, \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0440\u0438\u043d\u044f\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u0435\u0434\u0448\u0435\u0435 \u043a\u0430\u043a \u0434\u0430\u043d\u043d\u043e\u0441\u0442\u044c. \u041d\u043e \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442\u044c \u0441 GraphQL \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u043d\u0430 REST \u043d\u0430\u043c \u043d\u0435 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c, \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u043b \u043f\u043e\u0434 \u043d\u0430\u0448\u0438 \u0437\u0430\u0434\u0430\u0447\u0438, \u0430 \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u043d\u0430\u0439\u0442\u0438 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0443. \u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442, \u044d\u0442\u043e \u0414\u0430\u043d\u0438\u043b \u041c\u0430\u043a\u0441\u0438\u043c\u043e\u0432, \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442 ZeBrains, \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043f\u043e\u0447\u0435\u043c\u0443 \u043f\u043e\u0441\u043b\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u00ab\u0436\u0438\u0442\u044c \u0441\u0442\u0430\u043b\u043e \u043b\u0443\u0447\u0448\u0435, \u0436\u0438\u0442\u044c \u0441\u0442\u0430\u043b\u043e \u0432\u0435\u0441\u0435\u043b\u0435\u0435\u00bb(\u0441), \u0438 \u043d\u0430 \u0447\u0442\u043e \u043d\u0430\u0434\u043e \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043f\u0440\u0438 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u043d\u0430 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435.<\/p>\n<h2>\u0412\u044b\u0431\u043e\u0440 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438: \u043f\u043e\u0447\u0435\u043c\u0443 \u043c\u044b \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0438\u0441\u044c \u043d\u0430 Ariadne<\/h2>\n<p>\u0421\u0430\u043c\u044b\u0439 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u043c: \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 GraphQL \u0432 Starlette \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0431\u044b\u043b\u0430 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0430 \u043d\u0430 \u0431\u0430\u0437\u0435 Graphene, \u0430 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043e\u0434\u043d\u043e\u0439 \u0438\u0437 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u043b\u0430\u0441\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 starlette-graphene3. \u041d\u043e \u043c\u044b \u0443\u0436\u0435 \u0443\u0441\u043f\u0435\u043b\u0438 \u00ab\u043e\u0446\u0435\u043d\u0438\u0442\u044c\u00bb \u0438 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043b\u0430\u043a\u043e\u043d\u0438\u0447\u043d\u0443\u044e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e, \u0438 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043e\u0432 \u0434\u043b\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430, \u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c\u043e\u0441\u0442\u044c\u044e.\u00a0<\/p>\n<p>\u0410 \u043f\u043e\u0442\u043e\u043c\u0443 \u0441\u0432\u043e\u0439 \u0432\u044b\u0431\u043e\u0440 \u043c\u044b \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0438 \u043d\u0430 Ariadne:\u00a0<\/p>\n<ul>\n<li>\n<p>\u0423 \u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043e\u0431\u044a\u0435\u043c\u043d\u0430\u044f \u0438 \u043f\u043e\u043d\u044f\u0442\u043d\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f.<\/p>\n<\/li>\n<li>\n<p>\u041e\u043d \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d \u043d\u0430 \u0431\u0430\u0437\u0435 Apollo Federation, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432\u0441\u0435\u043c\u0438 \u043f\u043b\u044e\u0448\u043a\u0430\u043c\u0438 \u043e\u0442 \u043d\u0435\u0433\u043e.<\/p>\n<\/li>\n<li>\n<p>\u0410\u043a\u0442\u0438\u0432\u043d\u043e \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0438 \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0444\u043e\u0440\u043a\u0430\u043c\u0438 \u0438\u043b\u0438 \u0434\u043e\u0440\u0430\u0431\u043e\u0442\u043a\u0430\u043c\u0438 \u0447\u0443\u0436\u0438\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u2014 \u044d\u0442\u043e \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0434\u0443\u043a\u0442.<\/p>\n<\/li>\n<\/ul>\n<p>\u0413\u043b\u0430\u0432\u043d\u043e\u0435, \u0447\u0442\u043e \u043f\u0440\u0438\u0432\u043b\u0435\u043a\u043b\u043e \u043d\u0430\u0448\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u2014 \u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 Graphene Ariadne \u0438\u0434\u0435\u0442 \u043e\u0442 \u00ab\u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e\u00bb. \u041e\u0441\u043d\u043e\u0432\u0430 \u2014 graphql-\u0441\u0445\u0435\u043c\u0430, \u0430 \u043f\u043e \u043d\u0435\u0439 \u0441\u0442\u0440\u043e\u044f\u0442\u0441\u044f \u0432\u0441\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u043c\u0443\u0442\u0430\u0446\u0438\u0438 \u0438\u043b\u0438 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438.<\/p>\n<p>\u042d\u0442\u043e \u0434\u0430\u0435\u0442 \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0431\u043e\u043b\u044c\u0448\u0435 \u0433\u0438\u0431\u043a\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0442\u0438\u043f\u0430\u043c\u0438, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u044b\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043e\u0448\u0438\u0431\u043e\u043a \u0438 \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0435\u0435 \u0432 \u0441\u0445\u0435\u043c\u0435. \u0412 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0435 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043e\u0442 Graphene \u043d\u0430\u0431\u043e\u0440 \u043e\u0448\u0438\u0431\u043e\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0436\u0435\u0442 \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043c\u0443\u0442\u0430\u0446\u0438\u044f, \u043d\u0438\u043a\u0430\u043a \u043d\u0435 \u043e\u0442\u0440\u0430\u0436\u0435\u043d \u0432 \u0441\u0445\u0435\u043c\u0435 (\u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u0432\u044b \u043d\u0435 \u043f\u0430\u0442\u0447\u0438\u0442\u0435 \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u043c\u0435\u0442\u043e\u0434\u044b \u0435\u0435 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f), \u0430 \u0437\u043d\u0430\u0447\u0438\u0442 \u2014 \u043d\u0435\u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0443\u0435\u043c, \u0435\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043d\u0435\u0442 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0438\u043b\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438.\u00a0<\/p>\n<p>\u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, Ariadne \u043f\u0440\u0435\u043a\u0440\u0430\u0441\u043d\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043f\u043e\u0434\u043f\u0438\u0441\u043e\u043a, \u0438 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0442\u044f\u043d\u0443\u0442\u044c \u043b\u0438\u0448\u043d\u0438\u0435 \u043b\u0438\u0431\u044b (\u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u0442\u043e\u0433\u043e \u0436\u0435 Graphene). \u041f\u043b\u044e\u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0444\u0430\u0439\u043b\u043e\u0432 \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 GraphQL \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0431\u044b\u043b\u0430 \u043d\u0435 \u0441\u0430\u043c\u043e\u0439 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0435\u0439, \u0430 Ariadne \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0435\u0435 \u00ab\u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438\u00bb.<\/p>\n<h2>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043c\u0443\u0442\u0430\u0446\u0438\u0439, \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0438 \u043f\u043e\u0434\u043f\u0438\u0441\u043e\u043a<\/h2>\n<p>\u041f\u0438\u0441\u0430\u0442\u044c \u043e\u0447\u0435\u0440\u0435\u0434\u043d\u0443\u044e \u0441\u0442\u0430\u0442\u044c\u044e \u00ab\u043a\u0430\u043a \u043f\u0435\u0440\u0435\u0435\u0445\u0430\u0442\u044c \u0441 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0425\u0425\u0425 \u043d\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 YYY\u00bb \u2014 \u043d\u0435\u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e. \u0415\u0441\u043b\u0438 \u0432\u044b \u0434\u043e\u0447\u0438\u0442\u0430\u043b\u0438 \u0434\u043e \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u043c\u0435\u043d\u0442\u0430, \u0437\u043d\u0430\u0447\u0438\u0442 \u2014 \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438. \u041c\u044b \u0436\u0435 \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043e \u0442\u043e\u043c, \u043d\u0430 \u0447\u0442\u043e \u0441\u0442\u043e\u0438\u0442 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 <strong>\u043f\u043e\u0441\u043b\u0435<\/strong> \u043f\u0435\u0440\u0435\u0435\u0437\u0434\u0430, \u0447\u0442\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u0441\u044f \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432 \u043a\u043e\u0434\u0435. \u0420\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0442\u044c \u0431\u0443\u0434\u0435\u043c, \u043a\u0430\u043a \u0432\u043e\u0434\u0438\u0442\u0441\u044f, \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e todo-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0434\u0435\u043c\u043e-\u0432\u0435\u0440\u0441\u0438\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 <a href=\"https:\/\/github.com\/zebrainsteam\/fastapi-ariadne-example\" rel=\"noopener noreferrer nofollow\">\u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435<\/a>.<\/p>\n<h2>\u0417\u0430\u043f\u0440\u043e\u0441\u044b \u0438 \u043c\u0443\u0442\u0430\u0446\u0438\u0438<\/h2>\n<p>\u0412 GraphQL \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440\u043e\u0432 (\u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439), \u043a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0432 \u0441\u0435\u0431\u044f \u0434\u0432\u0430 \u043f\u043e\u0437\u0438\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430: obj \u0438 info. \u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0437 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 Ariadne: <\/p>\n<pre><code>def example_resolver(obj: Any, info: GraphQLResolveInfo):    return obj.do_something()   class FormResolver:    def __call__(self, obj: Any, info: GraphQLResolveInfo, **data):     . . .<\/code><\/pre>\n<p>\u0418\u0437 \u043a\u043e\u0434\u0430 \u0432\u044b\u0448\u0435 \u043c\u044b \u0432\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u043d\u0430\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u043a\u0430\u043a \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439, \u0442\u0430\u043a \u0438 \u041e\u041e\u041f \u043f\u043e\u0434\u0445\u043e\u0434. \u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u2014 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u0442\u0438\u043f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 Query \u0432 .graphql:<\/p>\n<p><strong>queries\/schema.graphql<\/strong><\/p>\n<pre><code>\u2026  type Query {     getTasks(userId: ID!): [TaskType]!     getTask(userId: ID!, taskId: ID!): TaskType! }  \u2026<\/code><\/pre>\n<p>\u041f\u0440\u0438\u0432\u044f\u0436\u0435\u043c \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440 \u043a \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u043c\u0443 \u0442\u0438\u043f\u0443 \u043f\u043e\u043b\u044f \u0441\u0445\u0435\u043c\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e ObjectType, \u0434\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0431\u0443\u0434\u0435\u0442 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434 .set_field(). \u041e\u043d \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0432 \u0441\u0435\u0431\u044f \u0434\u0432\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430: name, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u0442 \u0435\u0433\u043e \u0441 \u043e\u0434\u043d\u043e\u0438\u043c\u0435\u043d\u043d\u044b\u043c \u043f\u043e\u043b\u0435\u043c \u0441\u0445\u0435\u043c\u044b GraphQL \u0438, \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u043d\u0443\u0436\u043d\u044b\u0439 \u043d\u0430\u043c \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440.<\/p>\n<p><strong>queries\/__init__.py<\/strong><\/p>\n<pre><code>from ariadne import ObjectType  from ariadne_example.app.api.queries import task  queries = ObjectType(\"Query\")  queries.set_field(\"getTasks\", task.resolve_get_user_tasks) queries.set_field(\"getTask\", task.resolve_get_user_task_by_id)<\/code><\/pre>\n<p>\u0421\u0430\u043c\u0438 \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440\u044b \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0438\u0437 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0438\u0445 \u043d\u0430\u043f\u0438\u0448\u0435\u043c:<\/p>\n<p><strong>queries\/task.py<\/strong><\/p>\n<pre><code>import json from typing import Any, List  from ariadne import convert_kwargs_to_snake_case from graphql import GraphQLResolveInfo from graphql_relay.node.node import from_global_id from sqlmodel import select  from ariadne_example.app.db.session import Session, engine from ariadne_example.app.models import Task  @convert_kwargs_to_snake_case def resolve_get_user_tasks(         obj: Any,         info: GraphQLResolveInfo,         user_id: str, ) -> List[dict]:     \"\"\"Get user tasks\"\"\"     with Session(engine) as session:         local_user_id, _ = from_global_id(user_id)         statement = select(Task).where(Task.user_id == int(local_user_id))         tasks = session.execute(statement).scalars().all()         return [             Task(                 id=task.id,                 created_at=task.created_at,                 title=task.title,                 status=task.status,                 user_id=task.user_id             ).dict()             for task in tasks         ]  @convert_kwargs_to_snake_case def resolve_get_user_task_by_id(         obj: Any,         info: GraphQLResolveInfo,         task_id: str,         user_id: str, ) -> dict:     \"\"\"Get user task by task ID.\"\"\"     with Session(engine) as session:         local_task_id, _ = from_global_id(task_id)         local_user_id, _ = from_global_id(user_id)         statement = select(Task).where(Task.user_id == local_user_id, Task.id == local_task_id)         task = session.execute(statement).scalar_one()         return Task(             id=task.id,             created_at=task.created_at,             title=task.title,             status=task.status,             user_id=task.user_id,         ).dict()<\/code><\/pre>\n<p>\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0434\u043b\u044f todo-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430\u0431\u043e\u0440 \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440\u043e\u0432, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0441\u0435\u0445 \u0437\u0430\u0434\u0430\u0447 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u043b\u0438 \u043a\u0430\u043a\u0443\u044e-\u0442\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443.\u00a0<\/p>\n<p>\u0427\u0443\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u0435\u0435 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f \u043e\u0431\u0441\u0442\u043e\u0438\u0442 \u0441 \u043c\u0443\u0442\u0430\u0446\u0438\u044f\u043c\u0438. \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0432 Ariadne \u043e\u0441\u043d\u043e\u0432\u043e\u0439 \u0432\u0441\u0435\u0433\u043e \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0445\u0435\u043c\u0430 .graphql, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u043d\u0435\u0435 \u0442\u0438\u043f, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043d\u0430\u0448\u0438\u043c \u043c\u0443\u0442\u0430\u0446\u0438\u044f\u043c:<\/p>\n<p><strong>schema.graphql<\/strong><\/p>\n<pre><code>. . .  type Mutations {     createTask(userId: ID!, taskInput: TaskInput): Response     changeTaskStatus(taskId: ID!, newSatus: TaskStatusEnum): Response }  . . .<\/code><\/pre>\n<p>\u0414\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u0445\u0435\u043c\u044b \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043c \u0441 \u043c\u0443\u0442\u0430\u0446\u0438\u0435\u0439.<\/p>\n<p><strong>mutations\/__init__.py<\/strong><\/p>\n<pre><code>from ariadne import ObjectType  from .task import resolve_create_task  mutations = ObjectType('Mutation')  mutations.set_field('createTask', resolve_create_task)<\/code><\/pre>\n<p>\u0412 \u043a\u043e\u0434\u0435 \u0432\u044b\u0448\u0435 \u043c\u044b \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043b\u0438 \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 task.py, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0435\u0433\u043e \u043d\u0430\u043f\u0438\u0448\u0435\u043c.\u00a0<\/p>\n<p>\u0420\u0435\u0437\u043e\u043b\u0432\u0435\u0440\u044b \u043c\u0443\u0442\u0430\u0446\u0438\u0439 \u0432 Ariadne \u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0442 \u0432 \u0441\u0435\u0431\u044f \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b parent \u0438 info, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u043e\u0442\u043d\u043e\u0441\u044f\u0449\u0438\u0445\u0441\u044f \u043a \u043c\u0443\u0442\u0430\u0446\u0438\u0438, \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442 \u0434\u0430\u043d\u043d\u044b\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043a\u0430\u043a \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0412 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0434\u0432\u0430 \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440\u0430, \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0449\u0438\u0435 \u0437\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439:<\/p>\n<p><strong>mutations\/task.py<\/strong><\/p>\n<pre><code>from typing import Any  import sqlalchemy.exc from ariadne import convert_kwargs_to_snake_case from graphql.type.definition import GraphQLResolveInfo from graphql_relay.node.node import from_global_id from sqlmodel import select  from ariadne_example.app.db.session import Session, engine from ariadne_example.app.core.stru\u0441tures import TaskStatusEnum, TASK_QUEUES from ariadne_example.app.models import Task from ariadne_example.app.core.exceptions import NotFoundError  @convert_kwargs_to_snake_case def resolve_create_task(         obj: Any,         info: GraphQLResolveInfo,         user_id: str,         task_input: dict, ) -> int:     with Session(engine) as session:         local_user_id, _ = from_global_id(user_id)         try:             task = Task(                 title=task_input.get(\"title\"),                 created_at=task_input.get(\"created_at\"),                 status=task_input.get(\"status\"),                 user_id=local_user_id             )             session.add(task)             session.commit()             session.refresh(task)         except sqlalchemy.exc.IntegrityError:             raise NotFoundError(msg='\u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0441 \u0442\u0430\u043a\u0438\u043c user_id')         return task.id   @convert_kwargs_to_snake_case async def resolve_change_task_status(         obj: Any,         info: GraphQLResolveInfo,         new_status: TaskStatusEnum,         task_id: str, ) -> None:     with Session(engine) as session:         local_task_id, _ = from_global_id(task_id)         try:             statement = select(Task).where(Task.id == local_task_id)             task = session.execute(statement)             task.status = new_status             session.add(task)             session.commit()             session.refresh(task)         except sqlalchemy.exc.IntegrityError:             raise NotFoundError(msg='\u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0441 \u0442\u0430\u043a\u0438\u043c task_id')         for queue in TASK_QUEUES:             queue.put(task)<\/code><\/pre>\n<p>\u0422\u0443\u0442 \u0432\u0430\u0436\u043d\u043e \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u0430\u044f \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043c\u0443\u0442\u0430\u0446\u0438\u044f, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 \u0432\u0438\u0434\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e dict. \u0423 \u043d\u0430\u0441 \u043d\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c, \u043a\u0430\u043a \u0432 Graphene \u043a\u043b\u0430\u0441\u0441, \u0438 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0432 \u043d\u0435\u043c \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0435 \u043f\u043e\u043b\u044f:<\/p>\n<p><strong>(\u0432\u0430\u0440\u0438\u0430\u043d\u0442 graphene)task.py<\/strong><\/p>\n<pre><code>. . .  class CreateTask(graphene.Mutation):     task = graphene.Field(Task)      class Arguments:         user_id = graphene.ID()         input_data = graphene.Field()      def mutate(self, parent, info, user_id: str, input_data: Task):         local_user_id, _ = from_global_id(user_id)         session = get_session()         task = Task(title=input_data.get(\"title\"), user_id=local_user_id)         session.add(task)         session.commit()         session.refresh()         return CreateTask(task=task)   . . .<\/code><\/pre>\n<p>\u041d\u043e \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u043a \u0440\u0435\u0448\u0435\u043d\u0438\u044e \u044d\u0442\u043e\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c\u0441\u044f \u0441 \u0442\u0440\u0435\u0442\u044c\u0438\u043c \u0442\u0438\u043f\u043e\u043c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u2014 \u0441 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430\u043c\u0438.<\/p>\n<h2>\u041f\u043e\u0434\u043f\u0438\u0441\u043a\u0438 \u0432 Ariadne<\/h2>\n<p>\u041f\u043e \u0443\u0441\u0442\u043e\u044f\u0432\u0448\u0435\u0439\u0441\u044f \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u0438, \u043f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u0442\u0438\u043f \u0432 \u0441\u0445\u0435\u043c\u0435:<\/p>\n<p><strong>schema.graphql<\/strong><\/p>\n<pre><code>. . .  type Subscription {     taskStatusChanged: TaskType! }  . . .<\/code><\/pre>\n<p>\u041f\u043e\u0434\u043f\u0438\u0441\u043a\u0438 \u0441\u043b\u043e\u0436\u043d\u0435\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u043d\u0435 \u0434\u043b\u044f \u043e\u0434\u0438\u043d\u043e\u0447\u043d\u043e\u0433\u043e \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443, \u0430 \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0442\u044c \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445.\u00a0<\/p>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u044d\u0442\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u00ab\u043f\u043e \u043a\u043b\u0430\u0441\u0441\u0438\u043a\u0435\u00bb, \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c WebSockets. \u041d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0441\u043e\u043a\u0435\u0442 \u2014 \u043c\u0430\u043b\u043e, \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u043f\u0440\u0438 \u0438\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u043d\u0435 \u043f\u043e\u043c\u0435\u0448\u0430\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u0438 \u00ab\u043f\u0440\u0438\u0435\u043c\u043d\u0438\u043a\u00bb, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u044d\u0442\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u0441\u0442\u0443\u043f\u0430\u0442\u044c. <\/p>\n<p><strong>subscriptions.py<\/strong><\/p>\n<pre><code>import asyncio from typing import Any  from ariadne import convert_kwargs_to_snake_case, SubscriptionType from graphql import GraphQLResolveInfo  from ariadne_example.app.core.stru\u0441tures import TASK_QUEUES from ariadne_example.app.models import Task  subscription = SubscriptionType()   @subscription.source(\"taskStatusChanged\") @convert_kwargs_to_snake_case async def task_source(obj: Any, info: GraphQLResolveInfo):     queue = asyncio.Queue()     TASK_QUEUES.append(queue)     try:         while True:             change_task = await queue.get()             queue.task_done()             yield change_task     except asyncio.CancelledError:         TASK_QUEUES.remove(queue)         raise   @subscription.field(\"taskStatusChanged\") @convert_kwargs_to_snake_case def task_resolver(task: Task, info: Any):     return task<\/code><\/pre>\n<p>\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438 \u043c\u044b \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0432 subscription.source(&#171;taskStatusChanged&#187;), \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0441\u043e\u043a\u0435\u0442 \u0438 \u0442\u0440\u0430\u043d\u0441\u043b\u0438\u0440\u0443\u0435\u0442 \u043d\u0443\u0436\u043d\u044b\u0435 \u043d\u0430\u043c \u0434\u0430\u043d\u043d\u044b\u0435, \u0430 \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0438\u0445 \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e.<\/p>\n<h2>\u041e\u0448\u0438\u0431\u043a\u0438, \u044d\u043a\u0441\u0435\u043f\u0448\u0435\u043d\u044b \u0438 \u043c\u0438\u0434\u043b\u0432\u0430\u0440\u044b<\/h2>\n<p>\u0412\u0441\u0435, \u0438\u0437\u043b\u043e\u0436\u0435\u043d\u043d\u043e\u0435 \u0432\u044b\u0448\u0435 \u2014 \u0441\u0440\u043e\u0434\u043d\u0438 \u043e\u0431\u044b\u0447\u043d\u043e\u043c\u0443 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u043c\u0443 \u0437\u0430\u0434\u0430\u043d\u0438\u044e \u00ab\u043d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 todo \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439\u2026\u00bb. \u0410 \u0442\u0435\u043f\u0435\u0440\u044c \u2014 \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u0441\u0435\u0440\u044c\u0435\u0437\u043d\u043e \ud83d\ude42 <\/p>\n<p>\u041f\u0443\u043d\u043a\u0442 \u043f\u0435\u0440\u0432\u044b\u0439 \u2014 \u043c\u044b \u043e\u0442\u043b\u043e\u0436\u0438\u043b\u0438 \u00ab\u043d\u0430 \u0441\u043b\u0430\u0434\u043a\u043e\u0435\u00bb \u0432\u043e\u043f\u0440\u043e\u0441 \u0444\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u0435\u0437\u043d\u043e\u0439 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0432 \u043c\u0443\u0442\u0430\u0446\u0438\u044f\u0445. \u041f\u0443\u043d\u043a\u0442 \u0432\u0442\u043e\u0440\u043e\u0439 \u2014 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0441\u043b\u043e\u0439 \u043e\u0448\u0438\u0431\u043e\u043a GraphQL \u0432 \u0446\u0435\u043b\u043e\u043c \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0440\u043e\u043a\u0438\u043d\u0443\u0442\u044c \u043a\u043e\u0434 \u043e\u0448\u0438\u0431\u043a\u0438 \u0432 extensions \u0438 \u043f\u043e\u0442\u043e\u043c \u0435\u0433\u043e \u043e\u0442\u0442\u0443\u0434\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c, \u043d\u043e \u0434\u043b\u044f \u0444\u0440\u043e\u043d\u0442\u0430 \u0431\u044b\u043b\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u043e\u0439 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c,\u00a0 \u043a\u0430\u043a\u043e\u0439 \u0438\u043c\u0435\u043d\u043d\u043e \u0437\u0430\u043f\u0440\u043e\u0441 \u0438\u043b\u0438 \u043c\u0443\u0442\u0430\u0446\u0438\u044f \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043b\u0441\u044f \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439 \u0438 \u043a\u0430\u043a \u043d\u0430 \u044d\u0442\u0438 \u043e\u0448\u0438\u0431\u043a\u0438 \u0440\u0435\u0430\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c.<\/p>\n<p>\u0412\u0441\u043f\u043e\u043c\u043d\u0438\u043c, \u0447\u0442\u043e Ariadne \u043f\u0440\u043e\u043f\u0430\u0433\u0430\u043d\u0434\u0438\u0440\u0443\u0435\u0442 \u043f\u043e\u0434\u0445\u043e\u0434 \u00ab\u043e\u0442 \u0441\u0445\u0435\u043c\u044b \u043a \u043a\u043e\u0434\u0443\u00bb, \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432 .graphql \u043d\u0443\u0436\u043d\u044b\u0435 \u043d\u0430\u043c \u0442\u0438\u043f\u044b \u043e\u0448\u0438\u0431\u043e\u043a \u0438 \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u0432 \u0437\u0430\u0434\u0430\u0447:<\/p>\n<p><strong>schema.graphql<\/strong><\/p>\n<pre><code>. . .  enum ErrorTypeEnum {     SERVER_ERROR     NOT_FOUND_ERROR     VALIDATION_ERROR }  type ErrorType {     message: String     code: ErrorTypeEnum!     text: String }  enum TaskStatusEnum {     draft     in_process     delete     done }  . . .<\/code><\/pre>\n<p>\u0412\u044b\u043d\u0435\u0441\u0435\u043c \u043b\u043e\u0433\u0438\u043a\u0443 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0432 core \u0438 \u0437\u0430\u0434\u0430\u0434\u0438\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443:<\/p>\n<p><strong>core\/structures.py <\/strong><\/p>\n<pre><code>import enum from typing import Optional from dataclasses import dataclass  from ariadne import EnumType, ScalarType   class ErrorTypes(enum.Enum):     SERVER_ERROR = enum.auto()     NOT_FOUND_ERROR = enum.auto()     VALIDATION_ERROR = enum.auto()   @dataclass class ErrorScalar:     message: Optional[str]     code: ErrorTypes     text: Optional[str]   class TaskStatusEnum(enum.Enum):     draft = \"draft\"     in_process = \"in_process\"     delete = \"delete\"     done = \"done\"  task_type_enum = EnumType(\"TaskStatusEnum\", TaskStatusEnum) datetime_scalar = ScalarType(\"DateTime\")  @datetime_scalar.serializer def serialize_datetime(value):     return value.isoformat()  TASK_QUEUES = []<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u043a\u043b\u0430\u0441\u0441, \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0449\u0438\u0439 \u043e\u0441\u043c\u044b\u0441\u043b\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u0434 \u043e\u0448\u0438\u0431\u043a\u0438, \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0438 \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0442\u0435\u043a\u0441\u0442, \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u043e\u0448\u0438\u0431\u043e\u043a \u0438 \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u0432 \u0437\u0430\u0434\u0430\u0447\u0438. \u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0438\u0445 \u0432 \u0444\u0430\u0439\u043b \u044d\u043a\u0441\u0435\u043f\u0448\u0435\u043d\u043e\u0432:<\/p>\n<p><strong>core\/exceptions.py<\/strong><\/p>\n<pre><code>from typing import Optional, Dict, Any  from graphql import GraphQLError  from ariadne_example.app.core.stru\u0441tures import ErrorTypes, ErrorScalar   class BaseGraphQLError(GraphQLError):     def __init__(self, msg: str = \"Server Error\", extensions: Optional[Dict[str, Any]] = None):         if not hasattr(self, \"_extensions\"):             self._extensions = {\"code\": ErrorTypes.SERVER_ERROR.name}         if extensions is not None:             self._extensions = {**self._extensions, **extensions}         super().__init__(msg, extensions=self._extensions)      def parse(self) -> ErrorScalar:         parsed_exception = ErrorScalar(             message=self.extensions.get(\"user_message\"),             code=self.extensions.get(\"code\"),             text=self.message,         )         return parsed_exception   class ValidationError(BaseGraphQLError):     def __init__(self, msg: str, extensions: Optional[Dict[str, Any]] = None):         self._extensions = {\"code\": ErrorTypes.VALIDATION_ERROR.name}         super().__init__(msg, extensions=extensions)   class NotFoundError(BaseGraphQLError):     def __init__(self, msg: str, extensions: Optional[Dict[str, Any]] = None):         self._extensions = {\"code\": ErrorTypes.NOT_FOUND_ERROR.name}         super().__init__(msg, extensions=extensions)<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0448\u0438\u0431\u043a\u0443 GraphQL \u0438\u043b\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u0430 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u0439, \u043f\u0440\u0438\u0447\u0435\u043c \u0444\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0442\u0438\u043f \u043d\u0430\u0448\u0435\u0439 \u043e\u0448\u0438\u0431\u043a\u0438 \u0438 \u0435\u0435 \u043a\u043e\u0434. \u041d\u043e \u0437\u0430\u0447\u0435\u043c \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u0434\u043e\u0441\u0442\u0438\u0433\u043d\u0443\u0442\u043e\u043c? \u0412\u0441\u043f\u043e\u043c\u043d\u0438\u043c \u043e middlewares, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0442 \u043d\u0430\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0435 \u0448\u0430\u0433\u043e\u043c \u0440\u0430\u043d\u0435\u0435 \u044d\u043a\u0441\u0435\u043f\u0448\u0435\u043d\u044b:<\/p>\n<p><strong>core\/middlewares.py<\/strong><\/p>\n<pre><code>from ariadne.contrib.tracing.utils import is_introspection_field  from ariadne_example.app.core.exceptions import BaseGraphQLError   async def handle_error_middleware(resolver, obj, info, **args):     \"\"\"     \u0415\u0441\u043b\u0438 \u043d\u0430 \u044d\u0442\u0430\u043f\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043c\u0443\u0442\u0430\u0446\u0438\u0438 \u0438\u043b\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0431\u0440\u043e\u0448\u0435\u043d\u043e \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435,     \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u0438\u0442\u044c \u0438 \u0432\u044b\u0432\u0435\u0441\u0442\u0438 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043e\u0448\u0438\u0431\u043a\u0438.     \"\"\"     errors = []     value = {}      if is_introspection_field(info):         return resolver(obj, info, **args)      try:         value = await resolver(obj, info, **args)     except BaseGraphQLError as exc:         errors.append(exc.parse())         value = {**value, **{'errors': errors}}     except TypeError:         value = resolver(obj, info, **args)     return value<\/code><\/pre>\n<h2>\u041a\u0440\u0430\u0442\u043a\u0438\u0439 \u0438\u0442\u043e\u0433<\/h2>\n<p>\u0423\u0437\u043d\u0430\u0442\u044c \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043e\u0442\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u043e\u0442 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0438 \u043d\u0443\u0436\u043d\u043e\u0433\u043e \u0432\u0430\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u2014 \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u043d\u0435\u043f\u0440\u0438\u044f\u0442\u043d\u043e. \u041d\u043e \u044d\u0442\u043e \u0448\u0430\u043d\u0441 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0441\u0435 \u0442\u0430\u043a, \u043a\u0430\u043a \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u0438\u043c\u0435\u043d\u043d\u043e \u0432\u0430\u043c.\u00a0<\/p>\n<p>\u041d\u0430\u043c \u043d\u0435 \u0445\u0432\u0430\u0442\u0430\u043b\u043e \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043e\u0448\u0438\u0431\u043e\u043a, \u0447\u0442\u043e\u0431\u044b \u0444\u0440\u043e\u043d\u0442 \u043c\u043e\u0433 \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u043a\u0430\u0436\u0434\u0443\u044e \u0441 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u0438\u043b\u0438 \u043c\u0443\u0442\u0430\u0446\u0438\u0435\u0439. \u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f (\u043f\u0443\u0441\u0442\u044c \u0438 \u0432\u044b\u043d\u0443\u0436\u0434\u0435\u043d\u043d\u043e\u043c\u0443) \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0443 \u043d\u0430 Ariadne \u2014 \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0433\u0438\u0431\u043a\u043e \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0441\u0445\u0435\u043c\u0443 GraphQL \u043f\u043e\u0434 \u0441\u0432\u043e\u0438 \u0437\u0430\u0434\u0430\u0447\u0438, \u0447\u0435\u0440\u0435\u0437 \u043c\u0438\u0434\u043b\u0432\u0430\u0440\u044b \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043f\u0435\u0440\u0435\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0435 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438 \u043d\u0430 \u0444\u0440\u043e\u043d\u0442\u0435. \u0410 \u0441\u0430\u043c\u043e\u0435 \u0433\u043b\u0430\u0432\u043d\u043e\u0435 \u2014 \u0443 \u043d\u0430\u0441 \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438!<\/p>\n<p><a href=\"https:\/\/github.com\/zebrainsteam\/fastapi-ariadne-example\" rel=\"noopener noreferrer nofollow\">\u041f\u043e \u0441\u0441\u044b\u043b\u043a\u0435<\/a> \u2014 \u043e\u0431\u0430 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0442\u0435\u0441\u0442\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f todo: \u043d\u0430 \u0431\u0430\u0437\u0435\u00a0 Starlette, \u0438 \u043d\u0430 \u0431\u0430\u0437\u0435 Ariadne. \u0412\u043e\u043f\u0440\u043e\u0441\u044b, \u043f\u043e\u0436\u0435\u043b\u0430\u043d\u0438\u044f \u0438 \u043f\u0440\u043e\u043a\u043b\u044f\u0442\u0438\u044f \u2014 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u0442\u0438\u0442\u044c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445 \u0438\u043b\u0438 \u043a\u043e \u043c\u043d\u0435 \u0432 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c @maximovd<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/595777\/\"> https:\/\/habr.com\/ru\/articles\/595777\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0421 \u043d\u0435\u0434\u0430\u0432\u043d\u0435\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0432 Starlette \u043f\u0440\u0435\u043a\u0440\u0430\u0449\u0435\u043d\u0430 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 GraphQL. \u0422\u0430\u043a \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u0432\u044b, \u043a\u0430\u043a \u0438 \u043c\u044b, \u0437\u0430\u043d\u0438\u043c\u0430\u043b\u0438\u0441\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u043e\u0439 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u043d\u0430 FastAPI, \u0442\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0434\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 Starlette \u0432\u0430\u0441 \u043d\u0435\u043f\u0440\u0438\u044f\u0442\u043d\u043e \u0443\u0434\u0438\u0432\u0438\u043b\u0438.<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u041f\u0440\u0438\u0447\u0438\u043d\u044b, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u044d\u0442\u043e \u0441\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c, \u043d\u0435 \u0441\u0442\u043e\u043b\u044c \u0432\u0430\u0436\u043d\u044b, \u043e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0440\u0438\u043d\u044f\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u0435\u0434\u0448\u0435\u0435 \u043a\u0430\u043a \u0434\u0430\u043d\u043d\u043e\u0441\u0442\u044c. \u041d\u043e \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0438\u0442\u044c \u0441 GraphQL \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u043d\u0430 REST \u043d\u0430\u043c \u043d\u0435 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c, \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u043b \u043f\u043e\u0434 \u043d\u0430\u0448\u0438 \u0437\u0430\u0434\u0430\u0447\u0438, \u0430 \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u043d\u0430\u0439\u0442\u0438 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0443. \u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442, \u044d\u0442\u043e \u0414\u0430\u043d\u0438\u043b \u041c\u0430\u043a\u0441\u0438\u043c\u043e\u0432, \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0441\u0442 ZeBrains, \u0432 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443, \u043f\u043e\u0447\u0435\u043c\u0443 \u043f\u043e\u0441\u043b\u0435 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u00ab\u0436\u0438\u0442\u044c \u0441\u0442\u0430\u043b\u043e \u043b\u0443\u0447\u0448\u0435, \u0436\u0438\u0442\u044c \u0441\u0442\u0430\u043b\u043e \u0432\u0435\u0441\u0435\u043b\u0435\u0435\u00bb(\u0441), \u0438 \u043d\u0430 \u0447\u0442\u043e \u043d\u0430\u0434\u043e \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043f\u0440\u0438 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u043d\u0430 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435.<\/p>\n<h2>\u0412\u044b\u0431\u043e\u0440 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438: \u043f\u043e\u0447\u0435\u043c\u0443 \u043c\u044b \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0438\u0441\u044c \u043d\u0430 Ariadne<\/h2>\n<p>\u0421\u0430\u043c\u044b\u0439 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u043c: \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 GraphQL \u0432 Starlette \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0431\u044b\u043b\u0430 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0430 \u043d\u0430 \u0431\u0430\u0437\u0435 Graphene, \u0430 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043e\u0434\u043d\u043e\u0439 \u0438\u0437 \u0430\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u043b\u0430\u0441\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 starlette-graphene3. \u041d\u043e \u043c\u044b \u0443\u0436\u0435 \u0443\u0441\u043f\u0435\u043b\u0438 \u00ab\u043e\u0446\u0435\u043d\u0438\u0442\u044c\u00bb \u0438 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043b\u0430\u043a\u043e\u043d\u0438\u0447\u043d\u0443\u044e \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e, \u0438 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043e\u0432 \u0434\u043b\u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u043a\u043e\u0434\u0430, \u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c\u043e\u0441\u0442\u044c\u044e.\u00a0<\/p>\n<p>\u0410 \u043f\u043e\u0442\u043e\u043c\u0443 \u0441\u0432\u043e\u0439 \u0432\u044b\u0431\u043e\u0440 \u043c\u044b \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0438 \u043d\u0430 Ariadne:\u00a0<\/p>\n<ul>\n<li>\n<p>\u0423 \u043d\u0435\u0433\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043e\u0431\u044a\u0435\u043c\u043d\u0430\u044f \u0438 \u043f\u043e\u043d\u044f\u0442\u043d\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f.<\/p>\n<\/li>\n<li>\n<p>\u041e\u043d \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d \u043d\u0430 \u0431\u0430\u0437\u0435 Apollo Federation, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432\u0441\u0435\u043c\u0438 \u043f\u043b\u044e\u0448\u043a\u0430\u043c\u0438 \u043e\u0442 \u043d\u0435\u0433\u043e.<\/p>\n<\/li>\n<li>\n<p>\u0410\u043a\u0442\u0438\u0432\u043d\u043e \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0438 \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0444\u043e\u0440\u043a\u0430\u043c\u0438 \u0438\u043b\u0438 \u0434\u043e\u0440\u0430\u0431\u043e\u0442\u043a\u0430\u043c\u0438 \u0447\u0443\u0436\u0438\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u2014 \u044d\u0442\u043e \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043f\u0440\u043e\u0434\u0443\u043a\u0442.<\/p>\n<\/li>\n<\/ul>\n<p>\u0413\u043b\u0430\u0432\u043d\u043e\u0435, \u0447\u0442\u043e \u043f\u0440\u0438\u0432\u043b\u0435\u043a\u043b\u043e \u043d\u0430\u0448\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u2014 \u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 Graphene Ariadne \u0438\u0434\u0435\u0442 \u043e\u0442 \u00ab\u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e\u00bb. \u041e\u0441\u043d\u043e\u0432\u0430 \u2014 graphql-\u0441\u0445\u0435\u043c\u0430, \u0430 \u043f\u043e \u043d\u0435\u0439 \u0441\u0442\u0440\u043e\u044f\u0442\u0441\u044f \u0432\u0441\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b, \u043c\u0443\u0442\u0430\u0446\u0438\u0438 \u0438\u043b\u0438 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438.<\/p>\n<p>\u042d\u0442\u043e \u0434\u0430\u0435\u0442 \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0431\u043e\u043b\u044c\u0448\u0435 \u0433\u0438\u0431\u043a\u043e\u0441\u0442\u0438 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0442\u0438\u043f\u0430\u043c\u0438, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0432\u044b\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u043e\u0448\u0438\u0431\u043e\u043a \u0438 \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0435\u0435 \u0432 \u0441\u0445\u0435\u043c\u0435. \u0412 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0435 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043e\u0442 Graphene \u043d\u0430\u0431\u043e\u0440 \u043e\u0448\u0438\u0431\u043e\u043a, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0436\u0435\u0442 \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043c\u0443\u0442\u0430\u0446\u0438\u044f, \u043d\u0438\u043a\u0430\u043a \u043d\u0435 \u043e\u0442\u0440\u0430\u0436\u0435\u043d \u0432 \u0441\u0445\u0435\u043c\u0435 (\u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 \u0432\u044b \u043d\u0435 \u043f\u0430\u0442\u0447\u0438\u0442\u0435 \u0432\u0440\u0443\u0447\u043d\u0443\u044e \u043c\u0435\u0442\u043e\u0434\u044b \u0435\u0435 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f), \u0430 \u0437\u043d\u0430\u0447\u0438\u0442 \u2014 \u043d\u0435\u043f\u0440\u0435\u0434\u0441\u043a\u0430\u0437\u0443\u0435\u043c, \u0435\u0441\u043b\u0438 \u0443 \u0432\u0430\u0441 \u043d\u0435\u0442 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0438\u043b\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438.\u00a0<\/p>\n<p>\u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, Ariadne \u043f\u0440\u0435\u043a\u0440\u0430\u0441\u043d\u043e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043f\u043e\u0434\u043f\u0438\u0441\u043e\u043a, \u0438 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f \u0442\u044f\u043d\u0443\u0442\u044c \u043b\u0438\u0448\u043d\u0438\u0435 \u043b\u0438\u0431\u044b (\u0432 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u0442\u043e\u0433\u043e \u0436\u0435 Graphene). \u041f\u043b\u044e\u0441 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0444\u0430\u0439\u043b\u043e\u0432 \u043f\u0440\u0438 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 GraphQL \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0431\u044b\u043b\u0430 \u043d\u0435 \u0441\u0430\u043c\u043e\u0439 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0435\u0439, \u0430 Ariadne \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0435\u0435 \u00ab\u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438\u00bb.<\/p>\n<h2>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043c\u0443\u0442\u0430\u0446\u0438\u0439, \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0438 \u043f\u043e\u0434\u043f\u0438\u0441\u043e\u043a<\/h2>\n<p>\u041f\u0438\u0441\u0430\u0442\u044c \u043e\u0447\u0435\u0440\u0435\u0434\u043d\u0443\u044e \u0441\u0442\u0430\u0442\u044c\u044e \u00ab\u043a\u0430\u043a \u043f\u0435\u0440\u0435\u0435\u0445\u0430\u0442\u044c \u0441 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0425\u0425\u0425 \u043d\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 YYY\u00bb \u2014 \u043d\u0435\u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e. \u0415\u0441\u043b\u0438 \u0432\u044b \u0434\u043e\u0447\u0438\u0442\u0430\u043b\u0438 \u0434\u043e \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u043c\u0435\u043d\u0442\u0430, \u0437\u043d\u0430\u0447\u0438\u0442 \u2014 \u0432 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438. \u041c\u044b \u0436\u0435 \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u043c \u043e \u0442\u043e\u043c, \u043d\u0430 \u0447\u0442\u043e \u0441\u0442\u043e\u0438\u0442 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 <strong>\u043f\u043e\u0441\u043b\u0435<\/strong> \u043f\u0435\u0440\u0435\u0435\u0437\u0434\u0430, \u0447\u0442\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u0441\u044f \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0432 \u043a\u043e\u0434\u0435. \u0420\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0442\u044c \u0431\u0443\u0434\u0435\u043c, \u043a\u0430\u043a \u0432\u043e\u0434\u0438\u0442\u0441\u044f, \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u043e\u0433\u043e todo-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0434\u0435\u043c\u043e-\u0432\u0435\u0440\u0441\u0438\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0430 <a href=\"https:\/\/github.com\/zebrainsteam\/fastapi-ariadne-example\" rel=\"noopener noreferrer nofollow\">\u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435<\/a>.<\/p>\n<h2>\u0417\u0430\u043f\u0440\u043e\u0441\u044b \u0438 \u043c\u0443\u0442\u0430\u0446\u0438\u0438<\/h2>\n<p>\u0412 GraphQL \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440\u043e\u0432 (\u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439), \u043a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0432 \u0441\u0435\u0431\u044f \u0434\u0432\u0430 \u043f\u043e\u0437\u0438\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430: obj \u0438 info. \u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0437 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 Ariadne: <\/p>\n<pre><code>def example_resolver(obj: Any, info: GraphQLResolveInfo):    return obj.do_something()   class FormResolver:    def __call__(self, obj: Any, info: GraphQLResolveInfo, **data):     . . .<\/code><\/pre>\n<p>\u0418\u0437 \u043a\u043e\u0434\u0430 \u0432\u044b\u0448\u0435 \u043c\u044b \u0432\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u043d\u0430\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u043a\u0430\u043a \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439, \u0442\u0430\u043a \u0438 \u041e\u041e\u041f \u043f\u043e\u0434\u0445\u043e\u0434. \u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u2014 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u0442\u0438\u043f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 Query \u0432 .graphql:<\/p>\n<p><strong>queries\/schema.graphql<\/strong><\/p>\n<pre><code>\u2026  type Query {     getTasks(userId: ID!): [TaskType]!     getTask(userId: ID!, taskId: ID!): TaskType! }  \u2026<\/code><\/pre>\n<p>\u041f\u0440\u0438\u0432\u044f\u0436\u0435\u043c \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440 \u043a \u0434\u043e\u043f\u0443\u0441\u0442\u0438\u043c\u043e\u043c\u0443 \u0442\u0438\u043f\u0443 \u043f\u043e\u043b\u044f \u0441\u0445\u0435\u043c\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e ObjectType, \u0434\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0431\u0443\u0434\u0435\u0442 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434 .set_field(). \u041e\u043d \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0432 \u0441\u0435\u0431\u044f \u0434\u0432\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430: name, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u0435\u0442 \u0435\u0433\u043e \u0441 \u043e\u0434\u043d\u043e\u0438\u043c\u0435\u043d\u043d\u044b\u043c \u043f\u043e\u043b\u0435\u043c \u0441\u0445\u0435\u043c\u044b GraphQL \u0438, \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u043d\u0443\u0436\u043d\u044b\u0439 \u043d\u0430\u043c \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440.<\/p>\n<p><strong>queries\/__init__.py<\/strong><\/p>\n<pre><code>from ariadne import ObjectType  from ariadne_example.app.api.queries import task  queries = ObjectType(\"Query\")  queries.set_field(\"getTasks\", task.resolve_get_user_tasks) queries.set_field(\"getTask\", task.resolve_get_user_task_by_id)<\/code><\/pre>\n<p>\u0421\u0430\u043c\u0438 \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440\u044b \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0438\u0437 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0444\u0430\u0439\u043b\u0430, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0438\u0445 \u043d\u0430\u043f\u0438\u0448\u0435\u043c:<\/p>\n<p><strong>queries\/task.py<\/strong><\/p>\n<pre><code>import json from typing import Any, List  from ariadne import convert_kwargs_to_snake_case from graphql import GraphQLResolveInfo from graphql_relay.node.node import from_global_id from sqlmodel import select  from ariadne_example.app.db.session import Session, engine from ariadne_example.app.models import Task  @convert_kwargs_to_snake_case def resolve_get_user_tasks(         obj: Any,         info: GraphQLResolveInfo,         user_id: str, ) -> List[dict]:     \"\"\"Get user tasks\"\"\"     with Session(engine) as session:         local_user_id, _ = from_global_id(user_id)         statement = select(Task).where(Task.user_id == int(local_user_id))         tasks = session.execute(statement).scalars().all()         return [             Task(                 id=task.id,                 created_at=task.created_at,                 title=task.title,                 status=task.status,                 user_id=task.user_id             ).dict()             for task in tasks         ]  @convert_kwargs_to_snake_case def resolve_get_user_task_by_id(         obj: Any,         info: GraphQLResolveInfo,         task_id: str,         user_id: str, ) -> dict:     \"\"\"Get user task by task ID.\"\"\"     with Session(engine) as session:         local_task_id, _ = from_global_id(task_id)         local_user_id, _ = from_global_id(user_id)         statement = select(Task).where(Task.user_id == local_user_id, Task.id == local_task_id)         task = session.execute(statement).scalar_one()         return Task(             id=task.id,             created_at=task.created_at,             title=task.title,             status=task.status,             user_id=task.user_id,         ).dict()<\/code><\/pre>\n<p>\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u0434\u043b\u044f todo-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430\u0431\u043e\u0440 \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440\u043e\u0432, \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0441\u0435\u0445 \u0437\u0430\u0434\u0430\u0447 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u043b\u0438 \u043a\u0430\u043a\u0443\u044e-\u0442\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443.\u00a0<\/p>\n<p>\u0427\u0443\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u0435\u0435 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f \u043e\u0431\u0441\u0442\u043e\u0438\u0442 \u0441 \u043c\u0443\u0442\u0430\u0446\u0438\u044f\u043c\u0438. \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0432 Ariadne \u043e\u0441\u043d\u043e\u0432\u043e\u0439 \u0432\u0441\u0435\u0433\u043e \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0441\u0445\u0435\u043c\u0430 .graphql, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u043d\u0435\u0435 \u0442\u0438\u043f, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u043d\u0430\u0448\u0438\u043c \u043c\u0443\u0442\u0430\u0446\u0438\u044f\u043c:<\/p>\n<p><strong>schema.graphql<\/strong><\/p>\n<pre><code>. . .  type Mutations {     createTask(userId: ID!, taskInput: TaskInput): Response     changeTaskStatus(taskId: ID!, newSatus: TaskStatusEnum): Response }  . . .<\/code><\/pre>\n<p>\u0414\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0441\u0445\u0435\u043c\u044b \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043c \u0441 \u043c\u0443\u0442\u0430\u0446\u0438\u0435\u0439.<\/p>\n<p><strong>mutations\/__init__.py<\/strong><\/p>\n<pre><code>from ariadne import ObjectType  from .task import resolve_create_task  mutations = ObjectType('Mutation')  mutations.set_field('createTask', resolve_create_task)<\/code><\/pre>\n<p>\u0412 \u043a\u043e\u0434\u0435 \u0432\u044b\u0448\u0435 \u043c\u044b \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043b\u0438 \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440 \u0438\u0437 \u0444\u0430\u0439\u043b\u0430 task.py, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0435\u0433\u043e \u043d\u0430\u043f\u0438\u0448\u0435\u043c.\u00a0<\/p>\n<p>\u0420\u0435\u0437\u043e\u043b\u0432\u0435\u0440\u044b \u043c\u0443\u0442\u0430\u0446\u0438\u0439 \u0432 Ariadne \u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0442 \u0432 \u0441\u0435\u0431\u044f \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b parent \u0438 info, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432, \u043e\u0442\u043d\u043e\u0441\u044f\u0449\u0438\u0445\u0441\u044f \u043a \u043c\u0443\u0442\u0430\u0446\u0438\u0438, \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442 \u0434\u0430\u043d\u043d\u044b\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043a\u0430\u043a \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0412 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0434\u0432\u0430 \u0440\u0435\u0437\u043e\u043b\u0432\u0435\u0440\u0430, \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0449\u0438\u0435 \u0437\u0430 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0439:<\/p>\n<p><strong>mutations\/task.py<\/strong><\/p>\n<pre><code>from typing import Any  import sqlalchemy.exc from ariadne import convert_kwargs_to_snake_case from graphql.type.definition import GraphQLResolveInfo from graphql_relay.node.node import from_global_id from sqlmodel import select  from ariadne_example.app.db.session import Session, engine from ariadne_example.app.core.stru\u0441tures import TaskStatusEnum, TASK_QUEUES from ariadne_example.app.models import Task from ariadne_example.app.core.exceptions import NotFoundError  @convert_kwargs_to_snake_case def resolve_create_task(         obj: Any,         info: GraphQLResolveInfo,         user_id: str,         task_input: dict, ) -> int:     with Session(engine) as session:         local_user_id, _ = from_global_id(user_id)         try:             task = Task(                 title=task_input.get(\"title\"),                 created_at=task_input.get(\"created_at\"),                 status=task_input.get(\"status\"),                 user_id=local_user_id             )             session.add(task)             session.commit()             session.refresh(task)         except sqlalchemy.exc.IntegrityError:             raise NotFoundError(msg='\u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0441 \u0442\u0430\u043a\u0438\u043c user_id')         return task.id   @convert_kwargs_to_snake_case async def resolve_change_task_status(         obj: Any,         info: GraphQLResolveInfo,         new_status: TaskStatusEnum,         task_id: str, ) -> None:     with Session(engine) as session:         local_task_id, _ = from_global_id(task_id)         try:             statement = select(Task).where(Task.id == local_task_id)             task = session.execute(statement)             task.status = new_status             session.add(task)             session.commit()             session.refresh(task)         except sqlalchemy.exc.IntegrityError:             raise NotFoundError(msg='\u041d\u0435 \u043d\u0430\u0439\u0434\u0435\u043d\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0441 \u0442\u0430\u043a\u0438\u043c task_id')         for queue in TASK_QUEUES:             queue.put(task)<\/code><\/pre>\n<p>\u0422\u0443\u0442 \u0432\u0430\u0436\u043d\u043e \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u0430\u044f \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043c\u0443\u0442\u0430\u0446\u0438\u044f, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 \u0432\u0438\u0434\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e dict. \u0423 \u043d\u0430\u0441 \u043d\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c, \u043a\u0430\u043a \u0432 Graphene \u043a\u043b\u0430\u0441\u0441, \u0438 \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0432 \u043d\u0435\u043c \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u044b\u0435 \u043f\u043e\u043b\u044f:<\/p>\n<p><strong>(\u0432\u0430\u0440\u0438\u0430\u043d\u0442 graphene)task.py<\/strong><\/p>\n<pre><code>. . .  class CreateTask(graphene.Mutation):     task = graphene.Field(Task)      class Arguments:         user_id = graphene.ID()         input_data = graphene.Field()      def mutate(self, parent, info, user_id: str, input_data: Task):         local_user_id, _ = from_global_id(user_id)         session = get_session()         task = Task(title=input_data.get(\"title\"), user_id=local_user_id)         session.add(task)         session.commit()         session.refresh()         return CreateTask(task=task)   . . .<\/code><\/pre>\n<p>\u041d\u043e \u043f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u0442\u044c \u043a \u0440\u0435\u0448\u0435\u043d\u0438\u044e \u044d\u0442\u043e\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c\u0441\u044f \u0441 \u0442\u0440\u0435\u0442\u044c\u0438\u043c \u0442\u0438\u043f\u043e\u043c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u2014 \u0441 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430\u043c\u0438.<\/p>\n<h2>\u041f\u043e\u0434\u043f\u0438\u0441\u043a\u0438 \u0432 Ariadne<\/h2>\n<p>\u041f\u043e \u0443\u0441\u0442\u043e\u044f\u0432\u0448\u0435\u0439\u0441\u044f \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u0438, \u043f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u0442\u0438\u043f \u0432 \u0441\u0445\u0435\u043c\u0435:<\/p>\n<p><strong>schema.graphql<\/strong><\/p>\n<pre><code>. . .  type Subscription {     taskStatusChanged: TaskType! }  . . .<\/code><\/pre>\n<p>\u041f\u043e\u0434\u043f\u0438\u0441\u043a\u0438 \u0441\u043b\u043e\u0436\u043d\u0435\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0440\u0430\u0431\u043e\u0442\u0430\u044e\u0442 \u043d\u0435 \u0434\u043b\u044f \u043e\u0434\u0438\u043d\u043e\u0447\u043d\u043e\u0433\u043e \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443, \u0430 \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0442\u044c \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u044f\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043f\u0440\u0438 \u043a\u0430\u0436\u0434\u043e\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445.\u00a0<\/p>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u044d\u0442\u043e \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u00ab\u043f\u043e \u043a\u043b\u0430\u0441\u0441\u0438\u043a\u0435\u00bb, \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c WebSockets. \u041d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0442\u043a\u0440\u044b\u0442\u044c \u0441\u043e\u043a\u0435\u0442 \u2014 \u043c\u0430\u043b\u043e, \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-411863","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/411863","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=411863"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/411863\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=411863"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=411863"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=411863"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}