From 5778d121665a247f06d49c8b2751cbadc062aa0c Mon Sep 17 00:00:00 2001 From: Sergey Yarkov Date: Sat, 26 Sep 2020 14:17:56 +0300 Subject: [PATCH] format --- ...vanie-apollo-server-vmeste-s-typescript.md | 125 ++++++++++-------- gatsby-browser.js | 10 +- gatsby-config.js | 12 +- gatsby-node.esm.js | 2 +- gatsby-ssr.js | 12 +- src/components/Article/Article.js | 39 +++--- .../Articles/ArticleCard/ArticleCard.js | 25 ++-- src/components/Footer/Footer.js | 1 - src/components/Layout/Layout.js | 3 +- src/components/Newsletter/Newsletter.js | 10 +- src/components/SEO/SEO.js | 22 +-- src/hooks/useMoreArticlesQuery.js | 2 +- src/lib/amp.js | 29 ++-- src/pages/articles.js | 2 +- src/pages/index.js | 4 +- src/pages/snippets.js | 2 +- src/templates/article.amp.js | 23 +++- src/templates/article.js | 2 +- src/templates/category.js | 2 +- src/templates/tag.js | 2 +- 20 files changed, 193 insertions(+), 136 deletions(-) diff --git a/content/articles/2020-09-24_ispolzovanie-apollo-server-vmeste-s-typescript.md b/content/articles/2020-09-24_ispolzovanie-apollo-server-vmeste-s-typescript.md index 76bd060..3812e94 100644 --- a/content/articles/2020-09-24_ispolzovanie-apollo-server-vmeste-s-typescript.md +++ b/content/articles/2020-09-24_ispolzovanie-apollo-server-vmeste-s-typescript.md @@ -25,14 +25,14 @@ tags: ## Содержание -* [Описание списка зависимостей](#Описание-списка-зависимостей) -* [Структура проекта](#Структура-проекта) -* [Предварительная настройка](#Предварительная-настройка) -* [Создание Type Definitions](#Создание-Type-Definitions) -* [Создание Resolvers](#Создание-Resolvers) -* [Создание схемы](#Создание-схемы) -* [Использование Apollo Server Express](#Использование-Apollo-Server-Express) -* [Заключение](#Заключение) +- [Описание списка зависимостей](#Описание-списка-зависимостей) +- [Структура проекта](#Структура-проекта) +- [Предварительная настройка](#Предварительная-настройка) +- [Создание Type Definitions](#Создание-Type-Definitions) +- [Создание Resolvers](#Создание-Resolvers) +- [Создание схемы](#Создание-схемы) +- [Использование Apollo Server Express](#Использование-Apollo-Server-Express) +- [Заключение](#Заключение) 💡 Данное руководство расчитано на тех разработчиков, кто уже имел небольшой опыт с использованием технологий **Apollo Server** и **TypeScript**. @@ -40,44 +40,48 @@ tags: Для установки необходимых npm пакетов используйте команды ниже, но перед этим создайте папку проекта с любым названием и инициализируйте npm вместе с [конфигом typescript](https://www.typescriptlang.org/docs/handbook/compiler-options.html): -```bash +```bash mkdir typescript-apollo-server cd ./typescript-apollo-server // инициализация npm -npm init -y +npm init -y // установка dev зависимостей npm install -D @types/node prettier ts-node ts-node-dev typescript // установка основных зависимостей -npm install @types/graphql-depth-limit apollo-server-express cors dotenv express graphql graphql-depth-limit graphql-tools +npm install @types/graphql-depth-limit apollo-server-express cors dotenv express graphql graphql-depth-limit graphql-tools // инициализировать файл с настройками typescript npx tsc --init ``` + Теперь по порядку узнаем для чего нужны эти пакеты: **devDependencies:** + 1. **@typesnode** - устанавливаем type definitions node.js для нашего typescript **~717 kB** 2. **prettier** - этот пакет нужен для форматирования вашего кода, вы можете установить его по желанию **~9.08 MB** 3. **ts-node** - данные пакет предназначен для транспиляции typescript кода в javascript **~377 kB** -4. **ts-node-dev** - отладка кода typescript. этот пакет будет следить за вашими файлами **.ts** и перезагружать сервер при их изменении (можно использовать nodemon) **~57 kB** +4. **ts-node-dev** - отладка кода typescript. этот пакет будет следить за вашими файлами **.ts** и перезагружать сервер при их изменении (можно использовать nodemon) **~57 kB** 5. **typescript** - сам typescript **~56.8 MB** **dependencies:** + 1. **apollo-server-express** - GraphQL сервер вместе в веб-фреймворком Express.js **~70.1 kB** 2. **express** - веб-фреймворк Express.js **~208 kB** 3. **graphql** - среда выполнения запросов к нашей API **~1.92 MB** 4. **graphql-depth-limit** - защита он неограниченных запросов в API, подробнее [здесь](https://www.apollographql.com/blog/securing-your-graphql-api-from-malicious-queries-16130a324a6b/) 5. **@types/graphql-depth-limit** - type definitions graphql-depth-limit -6. **graphql-tools** - пакет, который будет создавать нашу GraphQL схему **~18 kB** +6. **graphql-tools** - пакет, который будет создавать нашу GraphQL схему **~18 kB** 7. **dotenv** - пакет, который будет загружать переменные среды из файла **.env** в **process.env** - **~23.1 kB** 8. **cors** - для включения кросс доменных запросов - **~20 kB** ## Структура проекта Теперь после того как все пакеты установлены, определимся со структурой проекта: + ```text . └── typescript-apollo-server @@ -103,13 +107,16 @@ npx tsc --init ## Предварительная настройка Перед тем как начать разрабатывать наш сервер, зайдем в **package.json** и установим скрипты запуска: + ```text "scripts": { "start": "ts-node index.ts", "server": "ts-node-dev --respawn index.ts" }, ``` + Теперь необходимо создать схему, создаем папку **graphql** и в ней также создаем еще две папки **resolvers** и **typeDefs**, также не забываем создать файл **schema.ts**: + ```bash mkdir graphql cd ./graphql @@ -134,6 +141,7 @@ touch Book.graphql Далее напишем наши типы. Обратите внимание что для удобства и разделения кода мы будем использовать импорты. Возможность использовать импорты нам предоставляет пакет **graphql-tools**. Для того чтобы импортировать файл **.graphql** ставится знак **#** и указывается путь. Главный файл **schema.graphql**: + ```graphql # ./graphql/typeDefs/schema.graphql @@ -147,6 +155,7 @@ type Query { ``` Файл **Book.graphql**: + ```graphql # ./graphql/typeDefs/Book.graphql @@ -157,6 +166,7 @@ type Book { ``` Файл **User.graphql**: + ```graphql # ./graphql/typeDefs/User.graphql @@ -191,10 +201,11 @@ cd .. Наконец прописываем обработчики: Файл **Books.ts**: + ```js // ./graphql/resolvers/Books/Books.ts -import { IResolvers } from 'graphql-tools'; +import { IResolvers } from "graphql-tools" interface BookType { id: string; @@ -202,17 +213,17 @@ interface BookType { } const books = [ - { id: '1', title: 'First Book' }, - { id: '2', title: 'Second Book' }, -]; + { id: "1", title: "First Book" }, + { id: "2", title: "Second Book" }, +] const booksResolver: IResolvers = { Query: { books: (): BookType[] => books, }, -}; +} -export default booksResolver; +export default booksResolver ``` Файл **Users.ts**: @@ -220,7 +231,7 @@ export default booksResolver; ```js // ./graphql/resolvers/Users/Users.ts -import { IResolvers } from 'graphql-tools'; +import { IResolvers } from "graphql-tools" interface UserType { id: string; @@ -228,17 +239,17 @@ interface UserType { } const users = [ - { id: '1', name: 'John' }, - { id: '2', name: 'Ivan' }, -]; + { id: "1", name: "John" }, + { id: "2", name: "Ivan" }, +] const usersResolver: IResolvers = { Query: { users: (): UserType[] => users, }, -}; +} -export default usersResolver; +export default usersResolver ``` Резолверы получились довольно простые и понятные, теперь нужно собрать их в один массив для дальнейшего экспорта. В файле **index.ts** прописываем следующее: @@ -246,18 +257,19 @@ export default usersResolver; ```js // ./graphql/resolvers/index.ts -import { IResolvers } from 'graphql-tools'; +import { IResolvers } from "graphql-tools" -import booksResolver from './Books/Books'; -import usersResolver from './Users/Users' +import booksResolver from "./Books/Books" +import usersResolver from "./Users/Users" -const resolvers: IResolvers[] = [usersResolver, booksResolver]; +const resolvers: IResolvers[] = [usersResolver, booksResolver] -export default resolvers; +export default resolvers ``` ## Создание схемы -Как говорилось ранее, пакет **graphql-tools** предназначен для создания схемы. Теперь перейдем в наш файл **schema.ts** и соберем нашу схему. + +Как говорилось ранее, пакет **graphql-tools** предназначен для создания схемы. Теперь перейдем в наш файл **schema.ts** и соберем нашу схему. Раньше для того чтобы работали наши graphql импорты мы применяли пакет **graphql-import**, но теперь его не нужно устанавливать так как он уже входит в состав **graphql-tools**. Подробнее об этом можно прочитать [здесь](https://www.graphql-tools.com/docs/migration-from-import). С помощью функций **loadSchemaSync**, **loadFilesSync**, и **addResolversToSchema** мы собираем нашу схему. Функция **mergeResolvers** предназначена для объединения наших резолверов. Подробнее о "**Resolvers merging**" [здесь](https://www.graphql-tools.com/docs/merge-resolvers/). Далее всё что нам остается сделать - это экспортировать нашу схему и написать сервер. @@ -265,20 +277,26 @@ export default resolvers; ```js // ./graphql/schema.ts -import { mergeResolvers, loadSchemaSync, loadFilesSync, GraphQLFileLoader, addResolversToSchema } from 'graphql-tools'; -import { GraphQLSchema } from 'graphql'; +import { + mergeResolvers, + loadSchemaSync, + loadFilesSync, + GraphQLFileLoader, + addResolversToSchema, +} from "graphql-tools" +import { GraphQLSchema } from "graphql" const schema = loadSchemaSync(`${__dirname}/typeDefs/schema.graphql`, { loaders: [new GraphQLFileLoader()], -}); +}) -const resolvers = loadFilesSync(`${__dirname}/resolvers`); +const resolvers = loadFilesSync(`${__dirname}/resolvers`) const schemaWithResolvers: GraphQLSchema = addResolversToSchema({ schema, resolvers: mergeResolvers(resolvers), -}); +}) -export default schemaWithResolvers; +export default schemaWithResolvers ``` ## Использование Apollo Server Express @@ -294,38 +312,38 @@ touch index.ts ```js // ./index.ts -import express, { Application, Request, Response } from 'express'; -import cors from 'cors'; -import depthLimit from 'graphql-depth-limit'; -import { ApolloServer } from 'apollo-server-express'; -import schema from './graphql/schema'; +import express, { Application, Request, Response } from "express" +import cors from "cors" +import depthLimit from "graphql-depth-limit" +import { ApolloServer } from "apollo-server-express" +import schema from "./graphql/schema" -const PORT = process.env.PORT || 4000; +const PORT = process.env.PORT || 4000 const startServer = (): void => { try { - const app: Application = express(); + const app: Application = express() const server: ApolloServer = new ApolloServer({ schema, validationRules: [depthLimit(10)], // выставляем правило deph limit = 10 playground: true, - }); + }) - app.use('*', cors()); - app.get('/', (req: Request, res: Response) => res.send('GraphQL API')); + app.use("*", cors()) + app.get("/", (req: Request, res: Response) => res.send("GraphQL API")) - server.applyMiddleware({ app }); + server.applyMiddleware({ app }) app.listen({ port: PORT }, () => console.log(`🚀 Server ready on port: ${PORT}`) - ); + ) } catch (err) { - console.log(`❌ Something went wrong: \n ${err}`); + console.log(`❌ Something went wrong: \n ${err}`) } -}; +} -startServer(); +startServer() ``` Как видим, ничего сложного нет, просто применяем функцию [applyMiddleware](https://www.apollographql.com/docs/apollo-server/api/apollo-server/#applymiddleware) для того чтобы соедениеть Apollo Server с фрэймворком Express.js. Далее можно протестировать наш сервер, запускаем команду **npm run start** и переходим по адресу **/graphql**. Для тестирования можно выполнить вот такой запрос: @@ -372,7 +390,6 @@ startServer(); } ``` - ## Заключение -Как видим, использование Apollo Server вместе с TypeScript оказалась совсем не сложной задачей, так как и Apollo Server и пакет graphql-import имеют поддержку typescript из коробки. \ No newline at end of file +Как видим, использование Apollo Server вместе с TypeScript оказалась совсем не сложной задачей, так как и Apollo Server и пакет graphql-import имеют поддержку typescript из коробки. diff --git a/gatsby-browser.js b/gatsby-browser.js index 1114378..1913a1a 100644 --- a/gatsby-browser.js +++ b/gatsby-browser.js @@ -3,7 +3,7 @@ import React from "react" import Layout from "./src/components/Layout/Layout" import "prismjs/themes/prism-tomorrow.css" -import 'prismjs/plugins/line-numbers/prism-line-numbers.css' +import "prismjs/plugins/line-numbers/prism-line-numbers.css" import "./src/styles/global.scss" import "./src/components/Header/Header.scss" @@ -30,8 +30,12 @@ export const shouldUpdateScroll = ({ routerProps: { location } }) => { } export const wrapPageElement = ({ element, props }) => { - const { location: { pathname } } = props - pathname.includes('/amp/article/') && props.pageContext.slug ? props.amp = true : props.amp = false + const { + location: { pathname }, + } = props + pathname.includes("/amp/article/") && props.pageContext.slug + ? (props.amp = true) + : (props.amp = false) return {element} } diff --git a/gatsby-config.js b/gatsby-config.js index 5f15076..89db1ab 100644 --- a/gatsby-config.js +++ b/gatsby-config.js @@ -161,12 +161,12 @@ module.exports = { { resolve: `gatsby-plugin-amp`, options: { - canonicalBaseUrl: 'https://devthread.ru/', - components: ['amp-sidebar', 'amp-accordion', 'amp-form'], - excludedPaths: ['/404*', '/'], - pathIdentifier: '/amp/', - relAmpHtmlPattern: '{{canonicalBaseUrl}}{{pathIdentifier}}{{pathname}}', - relCanonicalPattern: '{{canonicalBaseUrl}}{{pathname}}/', + canonicalBaseUrl: "https://devthread.ru/", + components: ["amp-sidebar", "amp-accordion", "amp-form"], + excludedPaths: ["/404*", "/"], + pathIdentifier: "/amp/", + relAmpHtmlPattern: "{{canonicalBaseUrl}}{{pathIdentifier}}{{pathname}}", + relCanonicalPattern: "{{canonicalBaseUrl}}{{pathname}}/", useAmpClientIdApi: false, }, }, diff --git a/gatsby-node.esm.js b/gatsby-node.esm.js index 18f2976..3852080 100644 --- a/gatsby-node.esm.js +++ b/gatsby-node.esm.js @@ -52,7 +52,7 @@ exports.createPages = async ({ actions, graphql }) => { slug: node.frontmatter.slug, }, }) - + createPage({ path: `amp/article/${node.frontmatter.slug}/`, component: articleTemplateAMP, diff --git a/gatsby-ssr.js b/gatsby-ssr.js index 006eed6..55b5741 100644 --- a/gatsby-ssr.js +++ b/gatsby-ssr.js @@ -40,7 +40,7 @@ const OnDark = () => { } export const onRenderBody = ({ pathname, setPreBodyComponents }) => { - if (pathname.includes('/amp/article/')) { + if (pathname.includes("/amp/article/")) { return } @@ -48,8 +48,12 @@ export const onRenderBody = ({ pathname, setPreBodyComponents }) => { } export const wrapPageElement = ({ element, props }) => { - const { location: { pathname } } = props - pathname.includes('/amp/article/') && props.pageContext.slug ? props.amp = true : props.amp = false - + const { + location: { pathname }, + } = props + pathname.includes("/amp/article/") && props.pageContext.slug + ? (props.amp = true) + : (props.amp = false) + return {element} } diff --git a/src/components/Article/Article.js b/src/components/Article/Article.js index 2bf9b4a..03ade43 100644 --- a/src/components/Article/Article.js +++ b/src/components/Article/Article.js @@ -18,7 +18,7 @@ import FacebookIcon from "../../images/facebook-icon__large.svg" const Article = ({ data: { slug, title, image, description, category, tags, time, date }, html, - amp + amp, }) => { const { siteMetadata: { @@ -40,10 +40,12 @@ const Article = ({
~ {time} мин
- {!amp ?
- - -
: null} + {!amp ? ( +
+ + +
+ ) : null}
@@ -93,19 +95,20 @@ const Article = ({ ))}
- {!amp - ? {title} - : {title} - } + {!amp ? ( + {title} + ) : ( + {title} + )}
diff --git a/src/components/Articles/ArticleCard/ArticleCard.js b/src/components/Articles/ArticleCard/ArticleCard.js index 0c5ad0c..84dc6cc 100644 --- a/src/components/Articles/ArticleCard/ArticleCard.js +++ b/src/components/Articles/ArticleCard/ArticleCard.js @@ -6,21 +6,26 @@ import { Link } from "gatsby" const ArticleCard = ({ data: { slug, title, image, category, description, tags }, - amp + amp, }) => { return (
- {!amp - ? {title} - : {title} - } + {!amp ? ( + {title} + ) : ( + {title} + )}
diff --git a/src/components/Footer/Footer.js b/src/components/Footer/Footer.js index 7b63702..60caed3 100644 --- a/src/components/Footer/Footer.js +++ b/src/components/Footer/Footer.js @@ -90,4 +90,3 @@ export default Footer Footer.propTypes = { amp: PropTypes.bool, } - diff --git a/src/components/Layout/Layout.js b/src/components/Layout/Layout.js index 7a94e17..9b6f759 100644 --- a/src/components/Layout/Layout.js +++ b/src/components/Layout/Layout.js @@ -2,10 +2,9 @@ import React from "react" import Header from "../Header/Header" import Footer from "../Footer/Footer" -import { HeaderAMP } from '../../lib/amp' +import { HeaderAMP } from "../../lib/amp" const Layout = ({ children, amp }) => { - /* amp is accelerated mobile pages */ diff --git a/src/components/Newsletter/Newsletter.js b/src/components/Newsletter/Newsletter.js index fc14e4f..e0e9a96 100644 --- a/src/components/Newsletter/Newsletter.js +++ b/src/components/Newsletter/Newsletter.js @@ -3,17 +3,21 @@ import React from "react" const Newsletter = () => { const submitHandler = e => { e.preventDefault() - } return (
-
📫{" "}Новостная рассылка
+
+ + 📫 + {" "} + Новостная рассылка +

Подпишитесь на новостную рассылку чтобы не пропустить вышедшие статьи

-
+
diff --git a/src/components/SEO/SEO.js b/src/components/SEO/SEO.js index 663b007..6a0e1cb 100644 --- a/src/components/SEO/SEO.js +++ b/src/components/SEO/SEO.js @@ -13,7 +13,7 @@ const SEO = ({ type, canonical, url, - amp + amp, }) => { const { pathname } = useLocation() const { siteMetadata } = useSiteMetadataQuery() @@ -71,15 +71,17 @@ const SEO = ({ - {!amp ? : null} + {!amp ? ( + + ) : null} ) } diff --git a/src/hooks/useMoreArticlesQuery.js b/src/hooks/useMoreArticlesQuery.js index 56d9b77..9ff56e8 100644 --- a/src/hooks/useMoreArticlesQuery.js +++ b/src/hooks/useMoreArticlesQuery.js @@ -17,7 +17,7 @@ const useMoreArticlesQuery = () => { title image { childImageSharp { - fluid(maxWidth: 650 quality: 100) { + fluid(maxWidth: 650, quality: 100) { ...GatsbyImageSharpFluid_withWebp } } diff --git a/src/lib/amp.js b/src/lib/amp.js index ea6da1c..7ff3bb3 100644 --- a/src/lib/amp.js +++ b/src/lib/amp.js @@ -1,8 +1,8 @@ -import React from 'react'; -import { Helmet } from 'react-helmet' -import useSiteMetadataQuery from '../hooks/useSiteMetadataQuery' +import React from "react" +import { Helmet } from "react-helmet" +import useSiteMetadataQuery from "../hooks/useSiteMetadataQuery" -import LogoIcon from '../images/logo-icon.svg' +import LogoIcon from "../images/logo-icon.svg" import TelegramIcon from "../images/telegram-icon.svg" import TwitterIcon from "../images/twitter-icon.svg" import YadzenIcon from "../images/yadzen-icon.svg" @@ -25,7 +25,7 @@ const HeaderAMP = () => { height: 100%; padding: 0px 24px; padding-top: 25px; - background-color: rgba(7,11,14,.99); + background-color: rgba(7, 11, 14, 0.99); width: 320px; } amp-sidebar nav li { @@ -79,9 +79,14 @@ const HeaderAMP = () => {
-