1
0
mirror of https://github.com/twirl/The-API-Book.git synced 2025-02-04 18:21:47 +02:00

Section III added

This commit is contained in:
Sergey Konstantinov 2022-08-27 23:07:11 +03:00
parent 943f929d4d
commit bd61d04bd7
67 changed files with 2778 additions and 472 deletions

View File

@ -5,15 +5,11 @@ This is the working repository for ‘The API’ book written by Sergey Konstant
## Current State and the Roadmap
Right now Section I (‘API Design’) and Section II (‘Backwards Compatibility’) are finished. The Sections are lacking readable schemes, I'll draw them later.
Right now all three section (‘The API Design’, ‘The Backwards Compatibility’, and ‘The API Product’) are finished. So the book is basically ready, I'm working on some cosmetics:
Section III ‘API as a Product’ will be discussing non-technical issues
* what for the APIs exist;
* monetizing APIs;
* making sure you understand users' needs and collect proper metrics;
* common practices, including AA issues and fraud problems;
* organizing docs portal;
* open source and community.
* adding readable schemes where it's appropriate;
* refactoring the ‘Describing Final Interfaces’ chapters;
* rephrasing and expanding the chapters on versioning and identifying users.
I also have more distant plans on adding two more subsections to Section I.
* Section Ia ‘JSON HTTP APIs’:

View File

@ -11,28 +11,26 @@ import puppeteer from 'puppeteer';
const args = process.argv.slice(2);
const dir = process.cwd();
const langs = (args[1] || 'en,ru').split(',');
const graphDir = resolve(dir, 'src', 'graphs');
const tmpDir = resolve(graphDir, 'tmp');
const langs = (args[0] || 'en,ru').split(',');
const target = args[1];
const srcDir = resolve(dir, 'src');
const graphDir = resolve(srcDir, 'graphs');
const tmpDir = resolve(graphDir, 'tmp');
if (!existsSync(tmpDir)) {
mkdirSync(tmpDir);
}
const targets = args[2]
? [args[2] + '.yaml']
: readdirSync(graphDir).filter((i) => i.endsWith('.yaml'));
build(langs, targets, srcDir, graphDir, tmpDir).then(
build(langs, srcDir, graphDir, tmpDir, target).then(
() => process.exit(0),
(e) => {
throw e;
}
);
async function build(langs, targets, srcDir, graphDir, tmpDir) {
async function build(langs, srcDir, graphDir, tmpDir, target) {
await buildL10n(langs, srcDir, tmpDir);
await buildGraphs(langs, targets, graphDir, tmpDir);
await buildGraphs(langs, srcDir, graphDir, tmpDir, target);
}
async function buildL10n(langs, srcDir, tmpDir) {
@ -54,49 +52,72 @@ async function buildL10n(langs, srcDir, tmpDir) {
);
}
async function buildGraphs(langs, targets, graphDir, tmpDir) {
const tasks = langs.reduce(
(tasks, lang) =>
tasks.concat(
targets.map((target) => {
lang, target;
})
),
[]
async function buildGraphs(langs, srcDir, graphDir, tmpDir, target) {
const tasks = target
? langs.map((lang) => ({
lang,
target
}))
: langs.reduce(
(tasks, lang) =>
tasks.concat(
readdirSync(resolve(srcDir, lang, 'graphs')).map(
(file) => ({
lang,
target: file.replace('.yaml', '')
})
)
),
[]
);
return Promise.all(
tasks.map(({ lang, target }) =>
buildGraph({
lang,
target,
yaml: readFileSync(
resolve(srcDir, lang, 'graphs', target + '.yaml'),
'utf-8'
),
graphDir,
tmpDir
})
)
);
return Promise.all(tasks.map(({lang, target}) => {
const yaml = readFileSync(resolve(graphDir, target))
});
for (const source of sources) {
console.log(`Building ${source}`);
for (const lang of langs) {
const inFile = `${indexFileUrl}?graph=${source}&lang=${lang}`;
console.log(` Open ${inFile}`);
const browser = await puppeteer.launch({
headless: true,
product: 'chrome',
defaultViewport: {
deviceScaleFactor: 2,
width: 1000,
height: 1000
}
});
const outFile = resolve(dir, 'src', 'img', `graph-${source}.png`);
const page = await browser.newPage();
await page.goto(inFile, {
waitUntil: 'networkidle0'
});
const body = await page.$('body');
await body.screenshot({
path: outFile,
type: 'png',
captureBeyondViewport: true
});
console.log(` ${outFile} saved`);
await browser.close();
}
}
}
async function buildGraph({ lang, target, yaml, graphDir, tmpDir }) {
const jsTmpFileName = `wrapped-${lang}-${target}.js`;
writeFileSync(
resolve(tmpDir, jsTmpFileName),
`document.querySelector('.mermaid').innerHTML = ${JSON.stringify(
yaml.replace(/\\n/g, '\\n')
)};`
);
// console.log(` Open ${inFile}`);
// const browser = await puppeteer.launch({
// headless: true,
// product: 'chrome',
// defaultViewport: {
// deviceScaleFactor: 2,
// width: 1000,
// height: 1000
// }
// });
// const outFile = resolve(dir, 'src', 'img', `graph-${source}.png`);
// const page = await browser.newPage();
// await page.goto(inFile, {
// waitUntil: 'networkidle0'
// });
// const body = await page.$('body');
// await body.screenshot({
// path: outFile,
// type: 'png',
// captureBeyondViewport: true
// });
// console.log(` ${outFile} saved`);
// await browser.close();
}

View File

@ -1,5 +1,6 @@
import { resolve as pathResolve } from 'path';
import templates from './src/templates.js';
//import { init, plugins } from '../The-Book-Builder/index.js';
import { init, plugins } from '@twirl/book-builder';
import { readFileSync, writeFileSync } from 'fs';
@ -23,6 +24,8 @@ const targets = (
return targets;
}, {});
const chapters = process.argv[4];
console.log(`Building langs: ${langsToBuild.join(', ')}`);
langsToBuild.forEach((lang) => {
init({
@ -43,18 +46,25 @@ langsToBuild.forEach((lang) => {
plugins.ast.h5Counter,
plugins.ast.aImg,
plugins.ast.imgSrcResolve,
plugins.ast.mermaid,
plugins.ast.ref,
plugins.ast.ghTableFix
]
},
htmlSourceValidator: {
validator: 'WHATWG',
ignore: ['heading-level', 'no-raw-characters', 'wcag/h37']
ignore: [
'heading-level',
'no-raw-characters',
'wcag/h37',
'no-missing-references'
]
},
html: {
postProcess: [plugins.html.imgDataUri]
}
}
},
chapters
}).then((builder) => {
Object.keys(targets).forEach((target) => {
if (target !== 'landing') {
@ -63,11 +73,12 @@ langsToBuild.forEach((lang) => {
pathResolve('docs', `${l10n[lang].file}.${lang}.${target}`)
);
} else {
const landingHtml = templates.landing(
builder.structure,
l10n[lang],
lang
);
const landingHtml = templates.landing({
structure: builder.structure,
l10n: l10n[lang],
lang,
templates
});
writeFileSync(
pathResolve('docs', l10n[lang].landingFile),
landingHtml

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

BIN
docs/assets/facebook.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -31,7 +31,9 @@ ul > li {
padding-left: 1em;
}
p, body > ul, h3 {
p,
body > ul,
h3 {
margin: 0 0 1em 0;
}
@ -66,12 +68,14 @@ a.github {
width: 1.2em;
}
a.linkedin {
a.linkedin,
a.share-linkedin {
background-image: url(linkedin.png);
width: 1.176em;
}
a.twitter {
a.twitter,
a.share-twitter {
background-image: url(twitter.svg);
width: 1.392em;
}
@ -104,6 +108,18 @@ a.kindle {
vertical-align: unset;
}
a.share-reddit {
background-image: url(reddit.png);
}
a.share-vk {
background-image: url(vk.svg);
}
a.share-facebook {
background-image: url(facebook.png);
}
img.header {
width: 80%;
}
@ -123,12 +139,12 @@ img.header {
text-align: center;
}
.about-me aside a, .about-me aside a:visited {
.about-me aside a,
.about-me aside a:visited {
color: inherit;
font-weight: bold;
}
.about-me aside img {
width: 100%;
}

BIN
docs/assets/reddit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

4
docs/assets/vk.svg Normal file
View File

@ -0,0 +1,4 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 23.04C0 12.1788 0 6.74826 3.37413 3.37413C6.74826 0 12.1788 0 23.04 0H24.96C35.8212 0 41.2517 0 44.6259 3.37413C48 6.74826 48 12.1788 48 23.04V24.96C48 35.8212 48 41.2517 44.6259 44.6259C41.2517 48 35.8212 48 24.96 48H23.04C12.1788 48 6.74826 48 3.37413 44.6259C0 41.2517 0 35.8212 0 24.96V23.04Z" fill="#0077FF"/>
<path d="M25.54 34.5801C14.6 34.5801 8.3601 27.0801 8.1001 14.6001H13.5801C13.7601 23.7601 17.8 27.6401 21 28.4401V14.6001H26.1602V22.5001C29.3202 22.1601 32.6398 18.5601 33.7598 14.6001H38.9199C38.0599 19.4801 34.4599 23.0801 31.8999 24.5601C34.4599 25.7601 38.5601 28.9001 40.1201 34.5801H34.4399C33.2199 30.7801 30.1802 27.8401 26.1602 27.4401V34.5801H25.54Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 808 B

View File

@ -10,7 +10,7 @@
</title>
<meta
name="description"
content="Designing APIs is a very special skill: API is a multiplier to both your opportunities and mistakes. This book is written to share the expertise and describe the best practices in the API design. In Section I, we'll discuss designing APIs as a concept: how to build the architecture properly, from high-level planning down to final interfaces. Section II is dedicated to expanding existing APIs in a backwards-compatible manner."
content="Designing APIs is a very special skill: API is a multiplier to both your opportunities and mistakes. This book is written to share the expertise and describe the best practices in designing and developing APIs. In Section I, we'll discuss the API architecture as a concept: how to build the hierarchy properly, from high-level planning down to final interfaces. Section II is dedicated to expanding existing APIs in a backwards-compatible manner. Finally, in Section III we will talk about the API as a product."
/>
<meta property="og:type" content="article" />
<meta
@ -19,7 +19,7 @@
/>
<meta
property="og:description"
content="Designing APIs is a very special skill: API is a multiplier to both your opportunities and mistakes. This book is written to share the expertise and describe the best practices in the API design. In Section I, we'll discuss designing APIs as a concept: how to build the architecture properly, from high-level planning down to final interfaces. Section II is dedicated to expanding existing APIs in a backwards-compatible manner."
content="Designing APIs is a very special skill: API is a multiplier to both your opportunities and mistakes. This book is written to share the expertise and describe the best practices in designing and developing APIs. In Section I, we'll discuss the API architecture as a concept: how to build the hierarchy properly, from high-level planning down to final interfaces. Section II is dedicated to expanding existing APIs in a backwards-compatible manner. Finally, in Section III we will talk about the API as a product."
/>
<meta property="og:image" content="assets/header.png" />
<meta
@ -38,12 +38,12 @@
<h1>Sergey Konstantinov<br/><span class="title">The API</span></h1>
<h2>Free e-book</h2>
<br />Subscribe for updates on <a class="github" href="https://github.com/twirl/The-API-Book"></a>
<br/>Follow me on <a class="linkedin" href="https://www.linkedin.com/in/twirl/"></a> &middot; <a class="twitter" href="https://twitter.com/blogovodoved"></a> &middot; <a class="medium" href="https://twirl.medium.com/">Medium</a>
<br />Support this work on <a class="patreon" href="https://www.patreon.com/yatwirl">Patreon</a> &middot; <a class="kindle" href="https://www.amazon.com/gp/product/B09RHH44S5/ref=dbs_a_def_rwt_hsch_vapi_tkin_p1_i0">buy Kindle version</a>
<br />⚙️⚙️⚙️
<br/>Follow me on <a class="linkedin" href="https://www.linkedin.com/in/twirl/"></a> · <a class="twitter" href="https://twitter.com/blogovodoved"></a> · <a class="medium" href="https://twirl.medium.com/">Medium</a>
<br />Support this work on <a class="patreon" href="https://www.patreon.com/yatwirl">Patreon</a> · <a class="kindle" href="https://www.amazon.com/gp/product/B09RHH44S5/ref=dbs_a_def_rwt_hsch_vapi_tkin_p1_i0">buy Kindle version</a>
<br />Share: <a class="share share-facebook" href="https://www.facebook.com/sharer.php?u=https%3A%2F%2Ftwirl.github.io%2FThe-API-Book%2F" target="_blank"></a> · <a class="share share-twitter" href="https://twitter.com/intent/tweet?text=The%20API%20by%20Sergey%20Konstantinov%20%E2%80%94%20a%20book%20about%20designing%20APIs%2C%20extending%20them%20and%20finding%20a%20proper%20place%20in%20the%20market&url=https%3A%2F%2Ftwirl.github.io%2FThe-API-Book%2F&hashtags=API%2CTheAPIBook&via=blogovodoved" target="_blank"></a> · <a class="share share-linkedin" href="https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Ftwirl.github.io%2FThe-API-Book%2F" target="_blank"></a> · <a class="share share-reddit" href="http://www.reddit.com/submit?url=https%3A%2F%2Ftwirl.github.io%2FThe-API-Book%2F&title=The%20API%20by%20Sergey%20Konstantinov%20%E2%80%94%20a%20book%20about%20designing%20APIs%2C%20extending%20them%20and%20finding%20a%20proper%20place%20in%20the%20market" target="_blank"></a><br/>⚙️⚙️⚙️
</nav>
<p>The API-first development is one of the hottest technical topics nowadays, since many companies started to realize that API serves as a multiplicator to their opportunities—but it also amplifies the design mistakes as well.</p>
<p>This book is dedicated to designing APIs: how to build the architecture properly, from a high-level planning down to final interfaces, and to extend API in a backwards-compatible manner.</p>
<p>This book is written to share the expertise and describe the best practices in designing and developing APIs. In Section I, we'll discuss the API architecture as a concept: how to build the hierarchy properly, from high-level planning down to final interfaces. Section II is dedicated to expanding existing APIs in a backwards-compatible manner. Finally, in Section III we will talk about the API as a product.</p>
<p>Illustration &amp; inspiration: <a href="https://www.instagram.com/art.mari.ka/">art.mari.ka</a>.</p>
<p>You might download ‘The API’ in <a href="API.en.pdf">PDF</a>, <a href="API.en.epub">EPUB</a>, or <a href="API.en.html">read it online</a>.
</p>
@ -83,21 +83,21 @@
</ul>
</li>
<li>
<h4>Section III. The API Product</h4>
<h4><a href="API.en.html#section-4">Section III. The API Product</a></h4>
<ul>
<li>Chapter 20. API as a Product</li>
<li>Chapter 21. API Business Models</li>
<li>Chapter 22. Developing a Product Vision</li>
<li>Chapter 23. Communicating with Developers</li>
<li>Chapter 24. Communicating with Business Owners</li>
<li>Chapter 25. API Services Range</li>
<li>Chapter 26. API Key Performance Indicators</li>
<li>Chapter 27. Identifying Users and Preventing Fraud</li>
<li>Chapter 28. IT-security</li>
<li>Chapter 29. Supporting API users</li>
<li>Chapter 30. Documentation</li>
<li>Chapter 31. Testing Environment</li>
<li>Chapter 32. Managing Expectations</li>
<li><a href="API.en.html#chapter-20">Chapter 20. API as a Product</a></li>
<li><a href="API.en.html#chapter-21">Chapter 21. API Business Models</a></li>
<li><a href="API.en.html#chapter-22">Chapter 22. Developing a Product Vision</a></li>
<li><a href="API.en.html#chapter-23">Chapter 23. Communicating with Developers</a></li>
<li><a href="API.en.html#chapter-24">Chapter 24. Communicating with Business Owners</a></li>
<li><a href="API.en.html#chapter-25">Chapter 25. API Services Range</a></li>
<li><a href="API.en.html#chapter-26">Chapter 26. API Key Performance Indicators</a></li>
<li><a href="API.en.html#chapter-27">Chapter 27. Identifying Users and Preventing Fraud</a></li>
<li><a href="API.en.html#chapter-28">Chapter 28. Technical Means of Preventing ToS Violations</a></li>
<li><a href="API.en.html#chapter-29">Chapter 29. Supporting customers</a></li>
<li><a href="API.en.html#chapter-30">Chapter 30. The Documentation</a></li>
<li><a href="API.en.html#chapter-31">Chapter 31. The Testing Environment</a></li>
<li><a href="API.en.html#chapter-32">Chapter 32. Managing expectations</a></li>
</ul>
</li>
</ul>

View File

@ -10,7 +10,7 @@
</title>
<meta
name="description"
content="Разработка API — особый навык: API является как мультипликатором ваших возможностей, так и мультипликатором ваших ошибок. Эта книга написана для того, чтобы поделиться опытом и изложить лучшие практики проектирования API. Книга состоит из двух больших разделов. В первом разделе мы поговорим о проектировании API на стадии разработки концепции — как грамотно выстроить архитектуру, от крупноблочного планирования до конечных интерфейсов. Второй раздел посвящён развитию существующих API с сохранением обратной совместимости"
content="Разработка API — особый навык: API является как мультипликатором ваших возможностей, так и мультипликатором ваших ошибок. Эта книга написана для того, чтобы поделиться опытом и изложить лучшие практики разработки API. В первом разделе мы поговорим о проектировании API: как грамотно выстроить архитектуру, от крупноблочного планирования до конечных интерфейсов. Второй раздел посвящён развитию существующих API с сохранением обратной совместимости. Наконец, в третьем разделе мы поговорим об API как о продукте."
/>
<meta property="og:type" content="article" />
<meta
@ -19,7 +19,7 @@
/>
<meta
property="og:description"
content="Разработка API — особый навык: API является как мультипликатором ваших возможностей, так и мультипликатором ваших ошибок. Эта книга написана для того, чтобы поделиться опытом и изложить лучшие практики проектирования API. Книга состоит из двух больших разделов. В первом разделе мы поговорим о проектировании API на стадии разработки концепции — как грамотно выстроить архитектуру, от крупноблочного планирования до конечных интерфейсов. Второй раздел посвящён развитию существующих API с сохранением обратной совместимости"
content="Разработка API — особый навык: API является как мультипликатором ваших возможностей, так и мультипликатором ваших ошибок. Эта книга написана для того, чтобы поделиться опытом и изложить лучшие практики разработки API. В первом разделе мы поговорим о проектировании API: как грамотно выстроить архитектуру, от крупноблочного планирования до конечных интерфейсов. Второй раздел посвящён развитию существующих API с сохранением обратной совместимости. Наконец, в третьем разделе мы поговорим об API как о продукте."
/>
<meta property="og:image" content="assets/header.png" />
<meta
@ -37,13 +37,13 @@
/><br />
<h1>Сергей Константинов<br/><span class="title">API</span></h1>
<h2>Бесплатная электронная книга</h2>
<br />Подпишитесь на обновления на <a class="habr" href="https://habr.com/ru/users/forgotten/posts/">Хабре</a>
<br />Подпишитесь на обновления на <a class="habr" href="https://habr.com/ru/users/forgotten/">Хабре</a>
<br/>Follow me on <a class="linkedin" href="https://www.linkedin.com/in/twirl/"></a> · <a class="twitter" href="https://twitter.com/blogovodoved"></a> · <a class="medium" href="https://twirl.medium.com/">Medium</a>
<br />Поддержите эту работу на <a class="patreon" href="https://www.patreon.com/yatwirl">Patreon</a>
<br />⚙️⚙️⚙️
<br />Поделиться: <a class="share share-vk" href="https://vk.com/share.php?url=https%3A%2F%2Ftwirl.github.io%2FThe-API-Book%2Findex.ru.html" target="_blank"></a> · <a class="share share-facebook" href="https://www.facebook.com/sharer.php?u=https%3A%2F%2Ftwirl.github.io%2FThe-API-Book%2Findex.ru.html" target="_blank"></a> · <a class="share share-twitter" href="https://twitter.com/intent/tweet?text=%C2%ABAPI%C2%BB%20%D0%A1%D0%B5%D1%80%D0%B3%D0%B5%D1%8F%20%D0%9A%D0%BE%D0%BD%D1%81%D1%82%D0%B0%D0%BD%D1%82%D0%B8%D0%BD%D0%BE%D0%B2%D0%B0%20%E2%80%94%20%D0%BA%D0%BD%D0%B8%D0%B3%D0%B0%20%D0%BE%20%D0%B4%D0%B8%D0%B7%D0%B0%D0%B9%D0%BD%D0%B5%20API%20%D0%B8%20%D0%B5%D0%B3%D0%BE%20%D0%BF%D1%80%D0%BE%D0%B4%D1%83%D0%BA%D1%82%D0%BE%D0%B2%D0%BE%D0%BC%20%D0%B8%20%D1%82%D0%B5%D1%85%D0%BD%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%BC%20%D1%80%D0%B0%D0%B7%D0%B2%D0%B8%D1%82%D0%B8%D0%B8&url=https%3A%2F%2Ftwirl.github.io%2FThe-API-Book%2Findex.ru.html&hashtags=API%2CTheAPIBook&via=blogovodoved" target="_blank"></a> · <a class="share share-linkedin" href="https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Ftwirl.github.io%2FThe-API-Book%2Findex.ru.html" target="_blank"></a> · <a class="share share-reddit" href="http://www.reddit.com/submit?url=https%3A%2F%2Ftwirl.github.io%2FThe-API-Book%2Findex.ru.html&title=%C2%ABAPI%C2%BB%20%D0%A1%D0%B5%D1%80%D0%B3%D0%B5%D1%8F%20%D0%9A%D0%BE%D0%BD%D1%81%D1%82%D0%B0%D0%BD%D1%82%D0%B8%D0%BD%D0%BE%D0%B2%D0%B0%20%E2%80%94%20%D0%BA%D0%BD%D0%B8%D0%B3%D0%B0%20%D0%BE%20%D0%B4%D0%B8%D0%B7%D0%B0%D0%B9%D0%BD%D0%B5%20API%20%D0%B8%20%D0%B5%D0%B3%D0%BE%20%D0%BF%D1%80%D0%BE%D0%B4%D1%83%D0%BA%D1%82%D0%BE%D0%B2%D0%BE%D0%BC%20%D0%B8%20%D1%82%D0%B5%D1%85%D0%BD%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%BC%20%D1%80%D0%B0%D0%B7%D0%B2%D0%B8%D1%82%D0%B8%D0%B8" target="_blank"></a><br/>⚙️⚙️⚙️
</nav>
<p>«API-first» подход — одна из самых горячих горячих тем в разработке программного обеспечения в наше время. Многие компании начали понимать, что API выступает мультипликатором их возможностей — но также умножает и допущенные ошибки.</p>
<p>Эта книга посвящена проектированию API: как правильно выстроить архитектуру, начиная с высокоуровневого планирования и заканчивая деталями реализации конкретных интерфейсов, и как развивать API, не нарушая обратную совместимость.</p>
<p>Эта книга написана для того, чтобы поделиться опытом и изложить лучшие практики разработки API. В первом разделе мы поговорим о проектировании API: как грамотно выстроить архитектуру, от крупноблочного планирования до конечных интерфейсов. Второй раздел посвящён развитию существующих API с сохранением обратной совместимости. Наконец, в третьем разделе мы поговорим об API как о продукте.</p>
<p>Иллюстрации и вдохновение: Maria Konstantinova &middot; <a href="https://www.instagram.com/art.mari.ka/">art.mari.ka</a>.</p>
<p>Вы можете скачать книгу «API» в формате <a href="API.ru.pdf">PDF</a>, <a href="API.ru.epub">EPUB</a>, или <a href="API.ru.html">прочитать её онлайе</a>.
</p>
@ -83,21 +83,21 @@
</ul>
</li>
<li>
<h4>Часть III. API как продукт</h4>
<h4><a href="API.ru.html#section-4">Раздел III. API как продукт</a></h4>
<ul>
<li>Глава 20. Продукт API</li>
<li>Глава 21. Бизнес-модели API</li>
<li>Глава 22. Формирование продуктового видения</li>
<li>Глава 23. Взаимодействие с разработчиками</li>
<li>Глава 24. Взаимодействие с бизнес-аудиторией</li>
<li>Глава 25. Линейка сервисов API</li>
<li>Глава 26. Ключевые показатели эффективности API</li>
<li>Глава 27. Идентификация пользователей и борьба с фродом</li>
<li>Глава 28. Информационная безопасность</li>
<li>Глава 29. Поддержка пользователей</li>
<li>Глава 30. Документация</li>
<li>Глава 31. Тестовая среда</li>
<li>Глава 32. Управление ожиданиями</li>
<li><a href="API.ru.html#chapter-20">Глава 20. Продукт API</a></li>
<li><a href="API.ru.html#chapter-21">Глава 21. Бизнес-модели API</a></li>
<li><a href="API.ru.html#chapter-22">Глава 22. Формирование продуктового видения</a></li>
<li><a href="API.ru.html#chapter-23">Глава 23. Взаимодействие с разработчиками</a></li>
<li><a href="API.ru.html#chapter-24">Глава 24. Взаимодействие с бизнес-аудиторией</a></li>
<li><a href="API.ru.html#chapter-25">Глава 25. Линейка сервисов API</a></li>
<li><a href="API.ru.html#chapter-26">Глава 26. Ключевые показатели эффективности API</a></li>
<li><a href="API.ru.html#chapter-27">Глава 27. Идентификация пользователей и борьба с фродом</a></li>
<li><a href="API.ru.html#chapter-28">Глава 28. Технические способы борьбы с несанкционированным доступом к API</a></li>
<li><a href="API.ru.html#chapter-29">Глава 29. Поддержка пользователей API</a></li>
<li><a href="API.ru.html#chapter-30">Глава 30. Документация</a></li>
<li><a href="API.ru.html#chapter-31">Глава 31. Тестовая среда</a></li>
<li><a href="API.ru.html#chapter-32">Глава 32. Управление ожиданиями</a></li>
</ul>
</li>
</ul>

View File

@ -5,13 +5,14 @@
"author": "Sergey Konstantinov <twirl-team@yandex.ru>",
"repository": "github.com:twirl/The-API-Book",
"devDependencies": {
"@twirl/book-builder": "0.0.12",
"@twirl/book-builder": "0.0.17",
"html-docx-js": "^0.3.1",
"nodemon": "^2.0.19",
"puppeteer": "^13.1.2"
},
"scripts": {
"build": "node build.mjs",
"build-docx": "node build-docx.mjs",
"build-graphs": "node build-graphs.mjs"
"build:watch": "nodemon -e md,js,mjs,css,json --exec npm run build ru html"
}
}

View File

@ -1,17 +1,28 @@
@page {
margin: 20mm 18mm 27mm 25mm;
size: 145mm 204mm;
}
@page:first {
margin: 0;
size: 102mm 157mm;
};
html,
body {
margin: 0;
padding: 0;
font-size: 14pt;
font-size: 12pt;
text-size-adjust: none;
}
.github-corner {
display: none;
pre {
margin: 0 0 1em 0;
width: calc(100% - 2px);
}
pre,
img {
margin-right: 0.1em;
margin: 1px;
}
h1,
@ -20,39 +31,40 @@ h3,
h4,
h5,
h6 {
page-break-after: never;
page-break-before: auto;
page-break-after: avoid;
}
p {
orphans: 5;
}
img {
margin-top: 1em;
page-break-after: never;
}
:root {
--main-font: 'PT Serif';
--alt-font: 'PT Serif';
--code-font: Inconsolata;
page-break-before: auto;
page-break-after: avoid;
}
.cover {
background-image: url(/cover_300dpi.png);
background-size: 100% auto;
background-position: 50% 50%;
background-repeat: no-repeat;
background-position: 50% 100%;
width: 100%;
height: 10.05in;
background-size: cover;
position: relative;
margin: 0;
padding: 0;
font-size: 4mm;
aspect-ratio: 102/150;
}
.annotation {
padding-top: 5in;
.cover h1 {
font-size: 12mm;
text-align: left;
padding: 10mm 20mm 10mm 20mm;
line-height: 15mm;
}
h1 {
padding: 1in 0.5in;
font-size: 40pt;
margin: 0;
.cover h1 .title {
font-size: 20mm;
}
p,
@ -61,12 +73,15 @@ ol {
orphans: 4;
}
@page {
margin: 0.5in;
size: 8.5in 11in;
.page-break,
.cover {
page-break-after: always;
}
@page:first {
margin: 0;
size: 7.5in 10in;
.no-page-break {
page-break-inside: avoid;
}
nav.page-main {
display: none;
}

View File

@ -1,12 +1,322 @@
h1 {
font-size: 48px;
background-image: url(/cover_96dpi.png);
background-repeat: no-repeat;
background-position: 50% 100%;
height: 1056px;
min-width: 300px;
max-width: 816px;
margin: 0 auto;
padding-top: 4em;
padding-left: 2em;
body > article {
position: relative;
}
.overflow-hidden {
overflow: hidden;
}
.cover {
background-image: url(/cover_96dpi.png);
background-position: 50% 25%;
background-repeat: no-repeat;
background-size: cover;
margin: 0;
aspect-ratio: 3/2;
}
.cover h1 {
margin: 0;
padding: 0;
font-size: 4vw;
text-align: center;
}
h1 .title {
font-size: 6vw;
}
nav.page-main {
height: 0;
position: sticky;
top: 0;
float: right;
margin-right: -2em;
}
nav.page-main ul {
padding: 0;
margin: 0;
list-style-type: none;
cursor: pointer;
}
nav.page-main ul li {
font-size: 1.8em;
width: 1em;
height: 1em;
line-height: 1em;
text-align: center;
}
nav.page-main ul li.para {
height: 2em;
line-height: 2em;
}
nav.page-main ul li.share,
.side-panel .share::before {
background-image: url(/img/share/share-light.svg);
background-size: 80% auto;
background-position: 50% 100%;
background-repeat: no-repeat;
}
.side-panel {
border-left: 1px solid #979b9f;
background-color: #e8e6e5;
border-radius: 0.2em;
position: sticky;
top: 0;
width: 80vw;
max-width: 30em;
height: 100vh;
float: right;
z-index: 1;
overflow: hidden auto;
padding: 0.5em 0.8em;
font-family: local-sans, sans-serif;
text-align: left;
}
.side-panel button.close {
position: absolute;
top: 0;
right: 0;
width: 2em;
height: 2em;
line-height: 2em;
border: none;
background: none;
cursor: pointer;
}
.side-panel button.close::after {
content: '✕';
font-size: 2em;
line-height: 1.5em;
}
.side-panel h1,
.side-panel h2,
.side-panel h3,
.side-panel h4 {
line-height: inherit;
font-weight: normal;
padding: inherit;
margin: inherit;
font-size: inherit;
text-indent: inherit;
word-wrap: break-word;
}
.side-panel h3.title {
padding: 0 0 1em 0;
font-weight: bold;
text-transform: uppercase;
}
.side-panel ul.share,
article ul.share {
margin: 0;
padding: 0;
list-style-type: none;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 0.2em;
}
.side-panel ul.share li a.share {
display: inline-block;
width: 1.5em;
height: 1.2em;
background-size: auto 80%;
cursor: pointer;
text-decoration: none;
background-repeat: no-repeat;
background-position: 50% 80%;
filter: grayscale(1);
}
.side-panel ul.share li a.share-facebook {
background-image: url(/img/share/facebook.png);
}
.side-panel ul.share li a.share-twitter {
background-image: url(/img/share/twitter.svg);
}
.side-panel ul.share li a.share-vk {
background-image: url(/img/share/vk.svg);
}
.side-panel ul.share li a.share-linkedin {
background-image: url(/img/share/linkedin.png);
}
.side-panel ul.share li a.share-reddit {
background-image: url(/img/share/reddit.png);
}
.side-panel ul.share li.copy-link {
white-space: nowrap;
}
.side-panel ul.share li.copy-link input {
height: 1.8em;
line-height: inherit;
padding: 0 0.5em;
background-color: #d3d8dc;
width: 20em;
border: 1px solid gray;
border-radius: 2px;
}
.copy-button {
padding: 0 0.2em;
height: 1.8em;
display: inline-block;
margin: 2px 1px;
line-height: inherit;
}
.copy-button::after {
content: '📋';
}
.side-panel h2.toc {
line-height: inherit;
padding: 1em 0 0 0;
font-weight: bold;
}
.fade {
position: sticky;
top: 0;
width: 100vw;
height: 100vh;
float: left;
background-color: rgba(0, 0, 0, 0.6);
z-index: 1;
}
body > article {
margin: 2em auto;
max-width: 60%;
}
a.anchor:before {
display: inline-block;
content: '¶';
width: 0.8em;
color: lightgray;
text-indent: 0;
}
h5 a.anchor:before {
color: transparent;
}
a.anchor:hover:before {
color: black;
}
h2:not(.toc),
h3 {
text-indent: -0.8em;
}
ul.references li p {
display: flex;
margin-left: -2em;
flex-grow: 2;
}
ul.references li p a.back-anchor {
width: 1.7em;
flex-shrink: 0;
}
@media screen {
.github-corner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
a.anchor:before {
display: inline-block;
content: '¶';
width: 0.8em;
color: lightgray;
text-indent: 0;
}
h5 a.anchor:before {
color: transparent;
}
a.anchor:hover:before {
color: black;
}
h2:not(.toc),
h3,
h5 {
text-indent: -0.8em;
}
@keyframes octocat-wave {
0%,
100% {
transform: rotate(0);
}
20%,
60% {
transform: rotate(-25deg);
}
40%,
80% {
transform: rotate(10deg);
}
}
}
@media screen and (max-width: 1000px) {
body > article {
padding: 2em;
margin: 0;
max-width: none;
text-align: left;
}
pre {
margin: 0;
padding: 0.2em;
}
ul,
ol {
padding-left: 1em;
}
ul.references li p {
margin-left: 0;
}
ul.references li p a.back-anchor {
width: auto;
}
table {
padding: 0;
font-size: 70%;
}
table td,
table th {
padding: 0;
}
.cover {
font-size: 2.8vw;
}
}

View File

@ -76,12 +76,25 @@
src: url(/fonts/RobotoMono-Regular.ttf);
}
html {
html,
body {
width: 100%;
margin: 0;
padding: 0;
}
.display-none {
display: none;
}
.overflow-hidden {
overflow: hidden;
}
.text-align-left {
text-align: left;
}
body,
h6 {
font-family: local-serif, serif;
@ -127,7 +140,7 @@ pre {
box-sizing: border-box;
page-break-inside: avoid;
overflow-x: auto;
font-size: 90%;
font-size: 80%;
}
img:not(.cc-by-nc-img) {
@ -195,12 +208,13 @@ h5 {
}
.annotation {
text-align: left;
text-align: justify;
}
ul.table-of-contents {
list-style-type: none;
padding-left: 0;
text-align: left;
}
ul.table-of-contents li {
@ -219,76 +233,3 @@ a.anchor {
color: inherit;
text-decoration: none;
}
@media screen {
body {
margin: 2em auto;
max-width: 60%;
}
.github-corner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
a.anchor:before {
display: inline-block;
content: '¶';
width: 0.8em;
color: lightgray;
text-indent: 0;
}
h5 a.anchor:before {
color: transparent;
}
a.anchor:hover:before {
color: black;
}
h2:not(.toc),
h3,
h5 {
text-indent: -0.8em;
}
@keyframes octocat-wave {
0%,
100% {
transform: rotate(0);
}
20%,
60% {
transform: rotate(-25deg);
}
40%,
80% {
transform: rotate(10deg);
}
}
}
@media screen and (max-width: 1000px) {
body {
padding: 2em;
margin: 0;
max-width: none;
text-align: left;
}
pre {
margin: 0;
padding: 0.2em;
}
ul,
ol {
padding-left: 1em;
}
.github-corner:hover .octo-arm {
animation: none;
}
.github-corner .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
}

View File

@ -3,7 +3,7 @@
"author": "Sergey Konstantinov",
"chapter": "Chapter",
"toc": "Table of Contents",
"description": "Designing APIs is a very special skill: API is a multiplier to both your opportunities and mistakes. This book is written to share the expertise and describe the best practices in the API design. In Section I, we'll discuss designing APIs as a concept: how to build the architecture properly, from high-level planning down to final interfaces. Section II is dedicated to expanding existing APIs in a backwards-compatible manner.",
"description": "Designing APIs is a very special skill: API is a multiplier to both your opportunities and mistakes. This book is written to share the expertise and describe the best practices in designing and developing APIs. In Section I, we'll discuss the API architecture as a concept: how to build the hierarchy properly, from high-level planning down to final interfaces. Section II is dedicated to expanding existing APIs in a backwards-compatible manner. Finally, in Section III we will talk about the API as a product.",
"locale": "en_US",
"file": "API",
"aboutMe": {
@ -18,6 +18,34 @@
"landingFile": "index.html",
"url": "https://twirl.github.io/The-API-Book/",
"favicon": "/img/favicon.png",
"sidePanel": {
"shareTo": "Share",
"copyLink": "Link",
"shareParameters": {
"url": "https://twirl.github.io/The-API-Book/",
"text": "The API by Sergey Konstantinov — a book about designing APIs, extending them and finding a proper place in the market",
"tags": "API,TheAPIBook",
"viaTwitter": "blogovodoved"
},
"services": [
{
"key": "facebook",
"link": "https://www.facebook.com/sharer.php?u=${url}"
},
{
"key": "twitter",
"link": "https://twitter.com/intent/tweet?text=${text}&url=${url}&hashtags=${tags}&via=${viaTwitter}"
},
{
"key": "linkedin",
"link": "https://www.linkedin.com/sharing/share-offsite/?url=${url}"
},
{
"key": "reddit",
"link": "http://www.reddit.com/submit?url=${url}&title=${text}"
}
]
},
"imageCredit": "Image Credit",
"links": {
"email": "yatwirl@gmail.com",
@ -41,8 +69,8 @@
"pageTitle": "Front Page",
"contents": [
"<p>The API-first development is one of the hottest technical topics nowadays, since many companies started to realize that API serves as a multiplicator to their opportunities—but it also amplifies the design mistakes as well.</p>",
"<p>This book is dedicated to designing APIs: how to build the architecture properly, from a high-level planning down to final interfaces, and to extend API in a backwards-compatible manner.</p>",
"<p>Illustrations &amp; inspiration by Maria Konstantinova &middot; <a href=\"https://www.instagram.com/art.mari.ka/\">art.mari.ka</a></p>",
"<p>This book is written to share the expertise and describe the best practices in designing and developing APIs. In Section I, we'll discuss the API architecture as a concept: how to build the hierarchy properly, from high-level planning down to final interfaces. Section II is dedicated to expanding existing APIs in a backwards-compatible manner. Finally, in Section III we will talk about the API as a product.</p>",
"<p class=\"text-align-left\">Illustrations &amp; inspiration by Maria Konstantinova &middot; <a href=\"https://www.instagram.com/art.mari.ka/\">art.mari.ka</a></p>",
"<img class=\"cc-by-nc-img\" alt=\"Creative Commons «Attribution-NonCommercial» Logo\" src=\"https://i.creativecommons.org/l/by-nc/4.0/88x31.png\"/>",
"<p class=\"cc-by-nc\">This book is distributed under the <a href=\"http://creativecommons.org/licenses/by-nc/4.0/\">Creative Commons Attribution-NonCommercial 4.0 International licence</a>.</p>"
]
@ -57,7 +85,7 @@
"support": ["patreon", "kindle"],
"content": [
"<p>The API-first development is one of the hottest technical topics nowadays, since many companies started to realize that API serves as a multiplicator to their opportunities—but it also amplifies the design mistakes as well.</p>",
"<p>This book is dedicated to designing APIs: how to build the architecture properly, from a high-level planning down to final interfaces, and to extend API in a backwards-compatible manner.</p>",
"<p>This book is written to share the expertise and describe the best practices in designing and developing APIs. In Section I, we'll discuss the API architecture as a concept: how to build the hierarchy properly, from high-level planning down to final interfaces. Section II is dedicated to expanding existing APIs in a backwards-compatible manner. Finally, in Section III we will talk about the API as a product.</p>",
"<p>Illustration &amp; inspiration: <a href=\"https://www.instagram.com/art.mari.ka/\">art.mari.ka</a>.</p>"
],
"download": "You might download ‘The API’ in",
@ -67,31 +95,5 @@
"footer": [
"<p>Книгу «API» можно <a href=\"index.ru.html\">читать по-русски</a>.</p>"
]
},
"contents": [
{ "chapters": ["", "", "", "", "", ""] },
{ "chapters": ["", "", "", "", "", ""] },
{ "chapters": ["", "", "", "", "", "", ""] },
{
"title": "Section III. The API Product",
"chapters": [
"API as a Product",
"API Business Models",
"Developing a Product Vision",
"Communicating with Developers",
"Communicating with Business Owners",
"API Services Range",
"API Key Performance Indicators",
"Identifying Users and Preventing Fraud",
"IT-security",
"Supporting API users",
"Documentation",
"Testing Environment",
"Managing Expectations"
]
}
],
"graphs": {
"topLevel": "Top-Level Objects"
}
}

View File

@ -1,20 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<link
rel="stylesheet"
type="text/css"
href="https://cdnjs.cloudflare.com/ajax/libs/jointjs/3.4.4/joint.css"
/>
<link rel="stylesheet" type="text/css" href="../css/graph.css" />
</head>
<body>
<!-- content -->
<div id="content"></div>
<!-- code -->
<script type="text/javascript" src="l10n.js"></script>
<script type="text/javascript" src="lib/lib.js"></script>
<div class="mermaid"></div>
<script type="text/javascript" src="tmp/l10n.js"></script>
<script type="text/javascript">
const params = document.location.href
.split('?', 2)[1]
@ -26,9 +17,9 @@
}, {});
document.write(
`<script type="text/javascript" src="${params.graph}.js">\<\/script>`
`<script type="text/javascript" src="tmp/wrapped-${params.lang}-${params.graph}.js">\<\/script>`
);
window.onload = () => render(lib, l10n[params.lang]);
</script>
<script src="lib/mermaid.min.js"></script>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -1,20 +0,0 @@
(function (global) {
const lib = (global.lib = {});
lib.group = ({ parent, height, width, title, left, top }) => {
const element = document.createElement('div');
element.classList.add('position-absolute');
element.innerHTML =
};
lib.entity = ({ parent, height, width, title, left, top, items }) => {
return new joint.shapes.standard.HeaderedRectangle()
.resize(width, height)
.position(left, top)
.attr('root/title', title)
.attr('header/fill', 'lightgray')
.attr('headerText/text', title)
.attr('bodyText/text', items.join('\n'))
.addTo(graph);
};
})(this);

3
src/graphs/lib/mermaid.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
src/graphs/tmp/l10n.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
document.querySelector('.mermaid').innerHTML = "flowchart TB\n subgraph API рецептов\n a1[[GET /v1/recipes]]\n end\n";

View File

@ -1,27 +0,0 @@
(function (global) {
global.render = (lib, l10n) => {
const { graph } = lib.paper({
width: 1000,
height: 320
});
// lib.group({
// graph,
// left: 10,
// top: 10,
// width: 980,
// height: 300,
// title: l10n.graphs.topLevel
// });
lib.entity({
graph,
left: 50,
top: 10,
width: 300,
height: 280,
title: 'Recipes Domain',
items: ['GET /recipe/{alias}']
});
};
})(this);

BIN
src/img/share/facebook.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

BIN
src/img/share/github.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
src/img/share/linkedin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

BIN
src/img/share/reddit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 481.6 481.6" style="enable-background:new 0 0 481.6 481.6;" xml:space="preserve">
<g>
<path d="M381.6,309.4c-27.7,0-52.4,13.2-68.2,33.6l-132.3-73.9c3.1-8.9,4.8-18.5,4.8-28.4c0-10-1.7-19.5-4.9-28.5l132.2-73.8
c15.7,20.5,40.5,33.8,68.3,33.8c47.4,0,86.1-38.6,86.1-86.1S429,0,381.5,0s-86.1,38.6-86.1,86.1c0,10,1.7,19.6,4.9,28.5
l-132.1,73.8c-15.7-20.6-40.5-33.8-68.3-33.8c-47.4,0-86.1,38.6-86.1,86.1s38.7,86.1,86.2,86.1c27.8,0,52.6-13.3,68.4-33.9
l132.2,73.9c-3.2,9-5,18.7-5,28.7c0,47.4,38.6,86.1,86.1,86.1s86.1-38.6,86.1-86.1S429.1,309.4,381.6,309.4z M381.6,27.1
c32.6,0,59.1,26.5,59.1,59.1s-26.5,59.1-59.1,59.1s-59.1-26.5-59.1-59.1S349.1,27.1,381.6,27.1z M100,299.8
c-32.6,0-59.1-26.5-59.1-59.1s26.5-59.1,59.1-59.1s59.1,26.5,59.1,59.1S132.5,299.8,100,299.8z M381.6,454.5
c-32.6,0-59.1-26.5-59.1-59.1c0-32.6,26.5-59.1,59.1-59.1s59.1,26.5,59.1,59.1C440.7,428,414.2,454.5,381.6,454.5z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

16
src/img/share/twitter.svg Normal file
View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Logo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 248 204" style="enable-background:new 0 0 248 204;" xml:space="preserve">
<style type="text/css">
.st0{fill:#1D9BF0;}
</style>
<g id="Logo_1_">
<path id="white_background" class="st0" d="M221.95,51.29c0.15,2.17,0.15,4.34,0.15,6.53c0,66.73-50.8,143.69-143.69,143.69v-0.04
C50.97,201.51,24.1,193.65,1,178.83c3.99,0.48,8,0.72,12.02,0.73c22.74,0.02,44.83-7.61,62.72-21.66
c-21.61-0.41-40.56-14.5-47.18-35.07c7.57,1.46,15.37,1.16,22.8-0.87C27.8,117.2,10.85,96.5,10.85,72.46c0-0.22,0-0.43,0-0.64
c7.02,3.91,14.88,6.08,22.92,6.32C11.58,63.31,4.74,33.79,18.14,10.71c25.64,31.55,63.47,50.73,104.08,52.76
c-4.07-17.54,1.49-35.92,14.61-48.25c20.34-19.12,52.33-18.14,71.45,2.19c11.31-2.23,22.15-6.38,32.07-12.26
c-3.77,11.69-11.66,21.62-22.2,27.93c10.01-1.18,19.79-3.86,29-7.95C240.37,35.29,231.83,44.14,221.95,51.29z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

4
src/img/share/vk.svg Normal file
View File

@ -0,0 +1,4 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 23.04C0 12.1788 0 6.74826 3.37413 3.37413C6.74826 0 12.1788 0 23.04 0H24.96C35.8212 0 41.2517 0 44.6259 3.37413C48 6.74826 48 12.1788 48 23.04V24.96C48 35.8212 48 41.2517 44.6259 44.6259C41.2517 48 35.8212 48 24.96 48H23.04C12.1788 48 6.74826 48 3.37413 44.6259C0 41.2517 0 35.8212 0 24.96V23.04Z" fill="#0077FF"/>
<path d="M25.54 34.5801C14.6 34.5801 8.3601 27.0801 8.1001 14.6001H13.5801C13.7601 23.7601 17.8 27.6401 21 28.4401V14.6001H26.1602V22.5001C29.3202 22.1601 32.6398 18.5601 33.7598 14.6001H38.9199C38.0599 19.4801 34.4599 23.0801 31.8999 24.5601C34.4599 25.7601 38.5601 28.9001 40.1201 34.5801H34.4399C33.2199 30.7801 30.1802 27.8401 26.1602 27.4401V34.5801H25.54Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 808 B

View File

@ -0,0 +1,4 @@
flowchart TB
subgraph API рецептов
a1[[GET /v1/recipes]]
end

View File

@ -3,7 +3,7 @@
"author": "Сергей Константинов",
"chapter": "Глава",
"toc": "Содержание",
"description": "Разработка API — особый навык: API является как мультипликатором ваших возможностей, так и мультипликатором ваших ошибок. Эта книга написана для того, чтобы поделиться опытом и изложить лучшие практики проектирования API. Книга состоит из двух больших разделов. В первом разделе мы поговорим о проектировании API на стадии разработки концепции — как грамотно выстроить архитектуру, от крупноблочного планирования до конечных интерфейсов. Второй раздел посвящён развитию существующих API с сохранением обратной совместимости",
"description": "Разработка API — особый навык: API является как мультипликатором ваших возможностей, так и мультипликатором ваших ошибок. Эта книга написана для того, чтобы поделиться опытом и изложить лучшие практики разработки API. В первом разделе мы поговорим о проектировании API: как грамотно выстроить архитектуру, от крупноблочного планирования до конечных интерфейсов. Второй раздел посвящён развитию существующих API с сохранением обратной совместимости. Наконец, в третьем разделе мы поговорим об API как о продукте.",
"locale": "ru_RU",
"file": "API",
"landingFile": "index.ru.html",
@ -18,6 +18,38 @@
},
"url": "https://twirl.github.io/The-API-Book/index.ru.html",
"favicon": "/img/favicon.png",
"sidePanel": {
"shareTo": "Поделиться",
"copyLink": "Ссылка",
"shareParameters": {
"url": "https://twirl.github.io/The-API-Book/index.ru.html",
"text": "«API» Сергея Константинова — книга о дизайне API и его продуктовом и техническом развитии",
"tags": "API,TheAPIBook",
"viaTwitter": "blogovodoved"
},
"services": [
{
"key": "vk",
"link": "https://vk.com/share.php?url=${url}"
},
{
"key": "facebook",
"link": "https://www.facebook.com/sharer.php?u=${url}"
},
{
"key": "twitter",
"link": "https://twitter.com/intent/tweet?text=${text}&url=${url}&hashtags=${tags}&via=${viaTwitter}"
},
{
"key": "linkedin",
"link": "https://www.linkedin.com/sharing/share-offsite/?url=${url}"
},
{
"key": "reddit",
"link": "http://www.reddit.com/submit?url=${url}&title=${text}"
}
]
},
"imageCredit": "Image Credit",
"links": {
"email": "yatwirl@gmail.com",
@ -29,7 +61,12 @@
"patreonTag": "Patreon",
"githubHref": "https://github.com/twirl/The-API-Book",
"githubString": "github.com/twirl/The-API-Book",
"habrHref": "https://habr.com/ru/users/forgotten/posts/",
"twitterHref": "https://twitter.com/blogovodoved",
"kindleHref": "https://www.amazon.com/gp/product/B09RHH44S5/ref=dbs_a_def_rwt_hsch_vapi_tkin_p1_i0",
"kindleTag": "buy Kindle version",
"mediumHref": "https://twirl.medium.com/",
"mediumTag": "Medium",
"habrHref": "https://habr.com/ru/users/forgotten/",
"habrTag": "Хабре"
},
"sourceCodeAt": "Исходный код доступен на",
@ -38,8 +75,8 @@
"pageTitle": "Титульный лист",
"contents": [
"<p>«API-first» подход — одна из самых горячих горячих тем в разработке программного обеспечения в наше время. Многие компании начали понимать, что API выступает мультипликатором их возможностей — но также умножает и допущенные ошибки.</p>",
"<p>Эта книга посвящена проектированию API: как правильно выстроить архитектуру, начиная с высокоуровневого планирования и заканчивая деталями реализации конкретных интерфейсов, и как развивать API, не нарушая обратную совместимость.</p>",
"<p>Иллюстрации и вдохновение: Maria Konstantinova &middot; <a href=\"https://www.instagram.com/art.mari.ka/\">art.mari.ka</a>.</p>",
"<p>Эта книга написана для того, чтобы поделиться опытом и изложить лучшие практики разработки API. В первом разделе мы поговорим о проектировании API: как грамотно выстроить архитектуру, от крупноблочного планирования до конечных интерфейсов. Второй раздел посвящён развитию существующих API с сохранением обратной совместимости. Наконец, в третьем разделе мы поговорим об API как о продукте.</p>",
"<p class=\"text-align-left\">Иллюстрации и вдохновение: Maria Konstantinova &middot; <a href=\"https://www.instagram.com/art.mari.ka/\">art.mari.ka</a>.</p>",
"<img class=\"cc-by-nc-img\" alt=\"Creative Commons «Attribution-NonCommercial» Logo\" src=\"https://i.creativecommons.org/l/by-nc/4.0/88x31.png\"/>",
"<p class=\"cc-by-nc\">Это произведение доступно по <a href=\"http://creativecommons.org/licenses/by-nc/4.0/\">лицензии Creative Commons «Attribution-NonCommercial» («Атрибуция — Некоммерческое использование») 4.0 Всемирная</a>.</p>"
]
@ -47,12 +84,14 @@
"landing": {
"subTitle": "Бесплатная электронная книга",
"subscribeOn": "Подпишитесь на обновления на",
"followOn": "Follow me on",
"follow": ["linkedin", "twitter", "medium"],
"updates": ["habr"],
"supportThisWork": "Поддержите эту работу на",
"support": ["patreon"],
"content": [
"<p>«API-first» подход — одна из самых горячих горячих тем в разработке программного обеспечения в наше время. Многие компании начали понимать, что API выступает мультипликатором их возможностей — но также умножает и допущенные ошибки.</p>",
"<p>Эта книга посвящена проектированию API: как правильно выстроить архитектуру, начиная с высокоуровневого планирования и заканчивая деталями реализации конкретных интерфейсов, и как развивать API, не нарушая обратную совместимость.</p>",
"<p>Эта книга написана для того, чтобы поделиться опытом и изложить лучшие практики разработки API. В первом разделе мы поговорим о проектировании API: как грамотно выстроить архитектуру, от крупноблочного планирования до конечных интерфейсов. Второй раздел посвящён развитию существующих API с сохранением обратной совместимости. Наконец, в третьем разделе мы поговорим об API как о продукте.</p>",
"<p>Иллюстрации и вдохновение: Maria Konstantinova &middot; <a href=\"https://www.instagram.com/art.mari.ka/\">art.mari.ka</a>.</p>"
],
"download": "Вы можете скачать книгу «API» в формате",
@ -62,28 +101,5 @@
"footer": [
"<p>You might also <a href=\"index.html\">read ‘The API’ in English</a>.</p>"
]
},
"contents": [
{ "chapters": ["", "", "", "", "", ""] },
{ "chapters": ["", "", "", "", "", ""] },
{ "chapters": ["", "", "", "", "", "", ""] },
{
"title": "Часть III. API как продукт",
"chapters": [
"Продукт API",
"Бизнес-модели API",
"Формирование продуктового видения",
"Взаимодействие с разработчиками",
"Взаимодействие с бизнес-аудиторией",
"Линейка сервисов API",
"Ключевые показатели эффективности API",
"Идентификация пользователей и борьба с фродом",
"Информационная безопасность",
"Поддержка пользователей",
"Документация",
"Тестовая среда",
"Управление ожиданиями"
]
}
]
}
}

71
src/scripts/sidebar.js Normal file
View File

@ -0,0 +1,71 @@
const activeElements = [
'.side-panel .close',
'nav.page-main',
'.side-panel .copy-button',
'.side-panel .copy-link input',
'.side-toc',
'.side-panel',
'.fade',
'html'
].map((selector) => document.querySelector(selector));
const [$close, $unfold, $copy, $input, $sideToc, $sidePanel, $fade, $html] =
activeElements;
document.addEventListener(
'click',
(e) => {
let node = e.target;
let isLink = false;
const activated = [];
while (node) {
if (node.tagName.toLowerCase() == 'a') {
isLink = true;
}
const match = activeElements.find((e) => e == node);
if (match) {
activated.push(match);
}
node = node.parentElement;
}
switch (activated[0]) {
case $unfold:
unfold();
break;
case $copy:
case $input:
copy();
break;
case $sideToc:
if (isLink) {
close();
}
break;
case $close:
case $fade:
case $html:
default:
close();
break;
}
},
false
);
function unfold() {
$sidePanel.classList.remove('display-none');
$fade.classList.remove('display-none');
$html.classList.add('overflow-hidden');
}
function close() {
$sidePanel.classList.add('display-none');
$fade.classList.add('display-none');
$html.classList.remove('overflow-hidden');
}
function copy() {
const value = $input.value;
$input.focus();
$input.select();
$input.setSelectionRange(0, value.length);
navigator.clipboard.writeText(value);
}

View File

@ -1,34 +1,110 @@
const templates = (module.exports = {
const { readFileSync } = require('fs');
const { resolve } = require('path');
module.exports = {
pageBreak: '<div class="page-break"></div>',
frontPage: (l10n) => `
mainContent: (content) => `<section class="main-content">
<nav class="page-main"><ul class="nav-folded">
<li class="share"></li>
<li class="para">§</li>
</ul></nav>${content}</section>`,
screenContent: (content, css, { structure, l10n, templates }) =>
`${templates.sidePanel({
structure,
l10n,
templates
})}<article>${content}</article>${templates.mainScript(l10n)}`,
mainScript: () =>
`<script>${readFileSync(resolve('./src/scripts/sidebar.js'))}</script>`,
sidePanel: ({
structure,
l10n,
templates
}) => `<div class="fade display-none"></div>
<aside class="side-panel display-none">
<h3 class="title">${l10n.author}. ${l10n.title}</h3>
${templates.shareControl({ l10n, templates })}
<section class="side-toc">${templates.toc(structure, l10n)}</section>
<button type="button" class="close"></button></aside>`,
shareControl: ({ l10n, templates }) => `<ul class="share text-align-left">
<li>${l10n.sidePanel.shareTo}:</li>
${l10n.sidePanel.services
.map(
({ key, link }) =>
`<li><a class="share share-${key}" href="${templates.shareLink(
link,
l10n.sidePanel.shareParameters
)}" target="_blank"> </a></li>`
)
.join('')}
<li class="copy-link">${
l10n.sidePanel.copyLink
}: <input type="text" value="${
l10n.sidePanel.shareParameters.url
}"/><button type="button" class="copy-button"></button></li>
</ul>`,
shareLinks: ({ l10n, templates }) =>
`${l10n.sidePanel.services
.map(
({ key, link }) =>
`<a class="share share-${key}" href="${templates.shareLink(
link,
l10n.sidePanel.shareParameters
)}" target="_blank">${key}</a>`
)
.join(' · ')}`,
frontPage: ({ templates, l10n }) => `
<div class="cover">
<h1>
<span class="author">${l10n.author}</span><br /><span class="title"
>${l10n.frontPage.title}</span
>
</h1>
</div><div class="page-break"></div><div class="annotation"><p>
</div><div class="page-break"></div><div class="annotation"><p class="text-align-left">
<strong>${l10n.author}. ${l10n.title}.</strong><br />
<a target="_blank" href="mailto:${l10n.links.email}">${
l10n.links.emailString
}</a> &middot; <a target="_blank" href="${l10n.links.linkedinHref}">${
}</a> &middot; <a target="_blank" href="${l10n.links.linkedinHref}">${
l10n.links.linkedinString
}</a> &middot; <a target="_blank" href="${l10n.links.patreonHref}">${
}</a> &middot; <a target="_blank" href="${l10n.links.patreonHref}">${
l10n.links.patreonString
}</a></p>
${l10n.frontPage.contents.join('\n')}
<p>${l10n.sourceCodeAt} <a target="_blank" href="${
l10n.links.githubHref
}">${l10n.links.githubString}</a></p>
</div><div class="page-break"></div>`,
<p class=\"text-align-left\">${
l10n.sourceCodeAt
} <a target="_blank" href="${l10n.links.githubHref}">${
l10n.links.githubString
}</a></p>
</div><p class="share text-align-left">${
l10n.sidePanel.shareTo
}: ${templates.shareLinks({
l10n,
templates
})}</p><div class="page-break"></div>`,
landing: (structure, l10n, lang) => {
shareLink: (link, parameters) => {
let result = link;
for (const [key, value] of Object.entries(parameters)) {
result = result.replace(
new RegExp(`\\$\\{${key}\\}`, 'g'),
encodeURIComponent(value)
);
}
return result;
},
landing: ({ structure, l10n, lang, templates }) => {
const link = (anchor, type = 'html') =>
`${encodeURIComponent(l10n.file)}.${lang}.${type}${
anchor ? '#' + anchor : ''
}`;
let chapterCounter = 0;
return `
<!DOCTYPE html>
<html>
@ -75,7 +151,7 @@ const templates = (module.exports = {
l10n.links[source + 'Href']
}">${l10n.links[source + 'Tag'] || ''}</a>`
)
.join(&middot; ')}
.join(· ')}
${
l10n.landing.follow && l10n.landing.follow.length
? `<br/>${l10n.landing.followOn} ${l10n.landing.follow
@ -85,7 +161,7 @@ const templates = (module.exports = {
l10n.links[source + 'Href']
}">${l10n.links[source + 'Tag'] || ''}</a>`
)
.join(&middot; ')}`
.join(· ')}`
: ''
}
<br />${l10n.landing.supportThisWork} ${l10n.landing.support
@ -95,8 +171,16 @@ const templates = (module.exports = {
l10n.links[source + 'Href']
}">${l10n.links[source + 'Tag'] || ''}</a>`
)
.join(' &middot; ')}
<br />
.join(' · ')}
<br />${l10n.sidePanel.shareTo}: ${l10n.sidePanel.services
.map(
({ key, link }) =>
`<a class="share share-${key}" href="${templates.shareLink(
link,
l10n.sidePanel.shareParameters
)}" target="_blank"></a>`
)
.join(' · ')}<br/>
</nav>
${l10n.landing.content.join('\n')}
<p>${l10n.landing.download} <a href="${link(
@ -107,37 +191,26 @@ const templates = (module.exports = {
} <a href="${link()}">${l10n.landing.readOnline}</a>.
</p>
<h3>${l10n.toc}</h3>
<ul>${l10n.contents
.map((section, i) => {
const written = structure.sections[i];
return `<li>
<h4>${
written
? `<a href="${link(written.anchor)}">${
written.title
}</a>`
: `${section.title}`
}</h4>
<ul>${structure.sections
.map(
(section) => `<li>
<h4><a href="${link(section.anchor)}">${section.title}</a></h4>
${
section.chapters.length
? `<ul>
${section.chapters
.map((chapter, j) => {
const writtenChapter =
written && written.chapters[j];
chapterCounter++;
return writtenChapter
? `<li><a href="${link(
writtenChapter.anchor
)}">${writtenChapter.title}</a></li>`
: `<li>${l10n.chapter} ${chapterCounter}. ${chapter}</li>`;
})
.map(
(chapter) =>
`<li><a href="${link(chapter.anchor)}">${
chapter.title
}</a></li>`
)
.join('\n')}
</ul>`
: ''
}
</li>`;
})
</li>`
)
.join('\n')}
</ul>
<p>${l10n.landing.license}</p>
@ -156,4 +229,4 @@ const templates = (module.exports = {
</body>
</html>`;
}
});
};