ã¯ããã«
ããã«ã¡ã¯ãã½ã¦ã¾ã¦Software Engineerã®@sue71ã§ããé£è¼ï¼ã¡ã«ã«ãªShops éçºã®è£å´ Vol.2ã®13æ¥ç®ãæ
å½ããã¦ããã ãã¾ãã
以åã¡ã«ã«ãªã¡ã«ã«ãªShopsã®æè¡ã¹ã¿ãã¯ã¨ããã®é¸å®çç±ã§BFFã®å®è£
ã«GraphQLãæ¡ç¨ãã¦ãããã¨ããä¼ããã¾ãããã¡ã«ã«ãªShopsããªãªã¼ã¹ãã¦ããç´åå¹´ãã£ãä»ãããã¾ã§ãæ¯ãè¿ã£ã¦GraphQLãµã¼ãã¼ãå®è£
ããä¸ã§ã®èª²é¡ããããããèãã¦ããã¨è¯ãé
ç®ãã¾ã¨ãã¦ã¿ã¾ãããã¾ããæ¬è¨äºã§ã¯ã¡ã«ã«ãªShopsã§GraphQLã®å®è£
ã¨ãã¦Apolloãæ¡ç¨ãã¦ãããããApolloã®å©ç¨ãåæã®è©±ãããã¤ãæ··å¨ãã¦ãã¾ããäºãã容赦ãã ããã
GraphQLã®èª¬æããã¡ã«ã«ãªShopsã®å®è£ æ¹æ³ã«é¢ãã¦ã¯ä»¥åãã¡ãã®è¨äºã§ç´¹ä»ãã¦ãã¾ãããã¡ããæ¯éã覧ãã ããã
ããã©ã¼ãã³ã¹èª²é¡
GraphQLã¯ãã¢ããªã±ã¼ã·ã§ã³ã®ãã¼ã¿ã¢ãã«ã表ããã°ã©ãæ§é ãããç¹å®ã®ãã¼ãããå§ã¾ãæ¨æ§é ãåãåºãããªã½ã¼ã¹ã®èªã¿è¾¼ã¿ã解決ãã¦ããã¨ããã³ã³ã»ããã§ä½ããã¦ãã¾ãã
ã¯ã©ã¤ã¢ã³ãã¯ã°ã©ãæ§é ã«æ²¿ã£ã¦æè»ã«ãã¼ã¿ãåå¾ã§ãã¾ãããæè»ããã«å¼ãèµ·ããããããã©ã¼ãã³ã¹èª²é¡ãããã¤ãããã¾ãã
N+1åé¡ã¸ã®å¯¾å¦
GraphQLã§ã¯æ¨æ§é ããã©ããªãããã®é½åº¦ãªã½ã¼ã¹ã®èªã¿è¾¼ã¿ã解決ãã¦ããã®ã§ããã°ãã°N+1åé¡ãèµ·ããã¾ãã
次ã®ã¯ã¨ãªã§ã¯productã100件åå¾ãã¦ãã¾ãããããããã®ãã¼ãã§shopãåå¾ãããã¨ã«ãªãã®ã§ãæç´ã«å®è£ ããã¨shopã®åå¾ã100åå¼ã³åºããããã¨ã«ãªãã¾ãã
query {
products(first: 100) {
edges {
node {
shop {
id
name
}
}
}
}
}
N+1åé¡ã®è§£æ±ºæ¹æ³ã¨ãã¦ããªã½ã¼ã¹ã®å
èªã¿ããªã½ã¼ã¹ã®é
延èªã¿è¾¼ã¿ãããã¾ãããã¡ã«ã«ãªShopsã§ã¯ã§ããã ãå®è£
ãåå©ç¨ãããããDataLoaderãå©ç¨ããé
延èªã¿è¾¼ã¿ãæ¡ç¨ãã¦ãã¾ãã
DataLoaderã®èª¬æãã¡ã«ã«ãªShopsã®å¯¾å¿æ¹æ³ã¯ãã¡ãã§è§£èª¬ãã¦ããã®ã§æ¯éã覧ãã ããã
Query complexityã®è¨æ¸¬
Query complexityã¨ã¯ã¯ã¨ãªã®è¤éããã³ã¹ãã¨ãã¦æ°å¤åãããã®ã§ããã³ã¹ãã®è¨ç®æ¹æ³ã¯æ確ãªä»æ§ãå®ãããã¦ããããã§ã¯ãªããã©ã¤ãã©ãªã«ãã£ã¦ã¾ã¡ã¾ã¡ã§ãã
ä¾ãã°ãGitHub API v4ã§ã¯ãã¼ãæ°ã«å¯¾ãã¦å¶éãè¨ãã¦ãããä¸è¨ã¯ã¨ãªã®å ´åã次ã®ããã«è¨ç®ããã¾ãã
complexity = productãã¼ãæ°(100) + shopãã¼ãæ°(10)
query {
shops(first: 10) { # ãã¼ãæ° 10
edges {
node {
products(first: 10) { # ãã¼ãæ° 10 x 10 = 100
edges {
node {
id
}
}
}
}
}
}
}
ã»ã¨ãã©ã®å ´åãã¢ããªã±ã¼ã·ã§ã³å´ã§ã«ã¹ã¿ãã¤ãºã§ããã®ã§ãã³ã¹ãã®é«ããã£ã¼ã«ãã«ã¯åå¥ã§é«ãæ°å¤ãå²ãå½ã¦ããªã©ã®æé©åãå¯è½ã§ããQuery complexityãäºãè¨æ¸¬ãã¦ãããã¨ã§ãç¹å®ã®ã¯ã¨ãªããã¼ã¸åä½ã®ã³ã¹ããå¶éãããå¯è¦åãã¦ããã©ã¼ãã³ã¹ãæèã§ãã¾ãã
ã¡ã«ã«ãªShopsã§ã¯ApolloServerã®ãã©ã°ã¤ã³ãå®è£ ããgraphql-query-complexityãå©ç¨ãã¦ãªã¯ã¨ã¹ããã¨ã«æ¤è¨¼ã»ãã®ã³ã°ãã¦ãã¾ãã
Persisted Queriesã®å©ç¨
GraphQLãå©ç¨ããå ´åãREST APIã¨å¯¾æ¯ãã¦æ¬¡ã®ãããªèª²é¡ãããã¾ãã
- GraphQLã®ã¯ã¨ãªã®è¤éãã«æ¯ä¾ãã¦ããªã¯ã¨ã¹ãããã£ãè¥å¤§åãã
- GraphQLã§ã¯POSTã使ç¨ããããHTTPãã£ãã·ã¥ã«ä¹ããªã
å®éã®ã¨ããGETãå©ç¨ãããã¨ãã§ããã®ã§ãããGraphQLã¯ã¨ãªãvariablesãå ¨ã¦ã¯ã¨ãªãã©ã¡ã¼ã¿ã«å«ããå ´åããµã¼ãã¼ãCDNã®å®è£ ã«ãã£ã¦ã¯URLã®ãµã¤ãºå¶éã«ã²ã£ãããå¯è½æ§ãããããããã®ã¾ã¾ã§ã¯CDNãªã©ã«ä¹ããã®ã¯é£ããã¨ãããã¨ã«ãªãã¾ãã
ããã§ã¯ã¨ãªã«å¯¾å¿ããIDãäºãç¨æããGraphQLãµã¼ãã¼ã®å段ã§IDã¨ã¯ã¨ãªã交æãããã¨ã§ãªã¯ã¨ã¹ããã©ã¡ã¼ã¿ãå°ããåãããã¨ããã®ãPersisted Queriesã§ãã交æã®ããã®ã¨ã³ããã¤ã³ããGETã§åãåãããã«ããã°ãCDNãªã©ã®ãã£ãã·ã¥ãµã¼ãã¼ã«ä¹ãããã¨ãå¯è½ã§ãã
ã¡ã«ã«ãªShopsã§ã¯ãNext.jsã«ãã£ã¦çæãããHTML / next data jsonãCDNã«ãã£ãã·ã¥ãã¦ããããã2ã«é¢ãã¦ã¯ä»ã®æããã«ããã¯ã«ãªã£ã¦ãã¾ããããããSSRãå©ç¨ãã¦ããªããã¼ã¸ã®ããã©ã¼ãã³ã¹æé©åãã許å¯ãã¦ããªãã¯ã¨ãªã®å¶éãªã©ã®æ©æµãããã®ã§ãå°å ¥ãæ¤è¨ãããæ©æ§ã®ä¸ã¤ã§ãã
ã¡ã«ã«ãªShopsã®ãã£ãã·ã¥æ¦ç¥ã«ã¤ãã¦ã¯ã¡ã«ã«ãªShopsã®ããã³ãã¨ã³ãã§ç´¹ä»ãã¦ããã®ã§æ¯éãã¡ãã御覧ãã ããã
ãã¼ã¿ã¹ãã¢ã«ãããã£ãã·ã¥
ã¢ããªã±ã¼ã·ã§ã³ã®ãã£ãã·ã¥ã«é¢ãã¦ã¯ä¸è¬çãªREST APIã¨å¤§ããªéãã¯ããã¾ããããªã½ã¼ã¹è§£æ±ºã®ã¿ã¤ãã³ã°ã§ãå¿ è¦ã«å¿ãã¦RedisãMemcacheãªã©ã®ãã¼ã¿ã¹ãã¢ãå©ç¨ãã¾ãããã ããDataLoaderãå©ç¨ãã¦ããå ´åã¯å°ã工夫ãå¿ è¦ã«ãªãã¾ããDataLoaderã®ãã£ãã·ã¥æ©æ§ã¯ãªã¯ã¨ã¹ãåä½ã®ãã£ãã·ã¥ãç®çã¨ãã¦ããã®ã§ãããç¨åº¦æå¹æéã®ãããã£ãã·ã¥ãå ±æãããå ´åã¯ããããåå¾ç¨ã®é¢æ°ã§å¥é調æ´ããå¿ è¦ãããã¾ãã
ã¡ã«ã«ãªShopsã§ã¯DataLoaderã®ãã£ãã·ã¥ã¬ã¤ã¤ã¼ã¨ãã¦Redisãæ¡ç¨ãã¦ãããDataLoaderã®ã©ããã¼ãå®è£ ãã¦ãã¾ãã次ã®ã³ã¼ãã¯å®éã®ãã®ã§ã¯ããã¾ãããã ioredis ãå©ç¨ããå ´åã®å¦çã®æµãã示ãã¦ãã¾ããRedisçµç±ã§ãã¼ã¿ãåå¾ãããã£ãã·ã¥ãåå¨ããªãå ´åã¯é常ã®DataLoaderãçµç±ãã¦ãã£ãã·ã¥ã®ä¿æã¨åå¾ãè¡ãã¾ãã
import DataLoader from 'dataloader';
import Redis from 'ioredis';
const client = new Redis();
// (1) é常ã®ãªã½ã¼ã¹è§£æ±ºãè¡ãDataLoader
const dataLoader = new DataLoader((keys) => {
// ãããã§ãªã½ã¼ã¹ãåå¾ããå¦ç
});
// (2) ãã£ãã·ã¥ã管çããDataLaoder
const redisLoader = new DataLoader(
(keys) =>
new Promise((resolve, reject) => {
client.mget(keys, (error, results) => {
// ...(çç¥)
Promise.all(
results.map((result, index) => {
// ...(çç¥)
// ãã£ãã·ã¥ãåå¨ããªãå ´åã(1)ãå©ç¨ãã¦ãªã½ã¼ã¹ã解決ãã
return dataLoader.load(keys[index]).then((value) => {
// ...(åå¾ããå¤ãRedisã«ã»ããããå¦ç)
return value;
});
})
).then(resolve);
});
})
);
// å¼ã³åºãå´ã¯(2)ãå©ç¨ãã¦ãªã½ã¼ã¹ã解決ãã
redisLoader.load("xxx");
ã»ãã¥ãªãã£èª²é¡
æ§ã ãªã¡ãªããã®ããGraphQLã§ãããã¯ã¨ãªã®æè»æ§ããéçºå¹çãä¸ããããã®æ©è½ãæå³ããèå¼±æ§ã«ç¹ããå¯è½æ§ãããã¾ããæ¬ç¯ã§ã¯ãä¸è¬çãªwebã¢ããªã±ã¼ã·ã§ã³ã®èå¼±æ§ã«å ãã¦å¯¾å¿ãå¿ è¦ãªé ç®ãã¾ã¨ãã¦ã¿ã¾ããã
ã¯ã¨ãªã®ã³ã¹ãå¶é
GraphQLã§ã¯ã¯ã©ã¤ã¢ã³ãã§ã¬ã¹ãã³ã¹ã®å 容ã決å®ã§ããã®ã§ãç°¡åã«è² è·ã®é«ãã¯ã¨ãªãæãããã¨ãã§ãã¾ããä¾ãã°æ¬¡ã®ããã«å¾ªç°åç §ãå©ç¨ããã¨ç¡éã®æ°ã®ãã¼ãããªã¯ã¨ã¹ããããã¨ãã§ãã¾ãã
query {
shop(id: "xxx") {
products(first: 100) {
shop {
products(first: 100) {
shop {
products(first: 100) {
shop {
â¦
}
}
}
}
}
}
}
}
Query depth
Query depthã¨ã¯ã¯ã¨ãªã®ã³ã¹ããæ·±ãã§è¡¨ãããã®ã§ããåè¿°ã®ã¯ã¨ãªã®ãããªãã¹ãã®æ·±ãã¯ã¨ãªã¯é常ã®ã¢ããªã±ã¼ã·ã§ã³ã§ä½¿ç¨ããããã¨ã¯ããå¾ãªãã®ã§ãæ·±ãã«ä¸å®ã®å¶éãè¨ãããã¨ã§ãæ¯è¼çç°¡åã«é«è² è·ãªQueryã®å®è¡ãé²ããã¨ãåºæ¥ã¾ãã
Query complexity
åç¯ã§ãç´¹ä»ããQuery complexityã§ãããã»ãã¥ãªãã£å¯¾çã¨ãã¦ãå©ç¨ã§ãã¾ããdepthã¨åæ§ã«è¨ç®ããã³ã¹ãã«ãããå¤ãè¨ãããã¨ã§ãããå¤ãã®ã±ã¼ã¹ã«å¯¾å¿ã§ãã¾ããä¾ãã°depthã§ã¯æ¬¡ã®ãããªã¯ã¨ãªã«ã¯å¯¾å¿ã§ãã¾ããã
query {
products(first: 100) {
highCostCalcurationResult # CPUè² è·ãé«ãå¦ç
}
}
highCostCalcurationResult
ã¯åä½ã®Productãã解決ããããã¨ãæå³ãã¦ããã®ã§é常å©ç¨ã§ã¯åé¡ããã¾ãããããã®ã¯ã¨ãªã®ããã«è¤æ°ãã¼ãã«å¯¾ãã¦å¼ã³åºãããã¨ãµã¼ãã¼ã®ãªã½ã¼ã¹ãæå³çã«æ¯æ¸ããããã¨ãã§ãã¾ãã
ããç¨åº¦æè»ã«å¶éãè¨ããããä¸æ¹ã§ãã³ã¹ããé©åã«ç®¡çãã妥å½ãªãããå¤ãå²ãåºãã®ã¯é£ããé¢ãããã¾ããã¾ãã¯ãã®ã³ã°ç®çã§éå ¥ãã¦å¾ã ã«æé©åãã¦ãã®ãè¯ãããããã¾ããã
Query rate limit
Query complexityã«ããå¶éã¯ä¸åº¦ã®ãªã¯ã¨ã¹ãã«å¯¾ãã¦ãããå¶éã§ãããç®åºããã³ã¹ããå©ç¨ãã¦æéåä½ã§å¶éãè¨ããQuery rate limitã¨ããæ¹æ³ãããã¾ãã
GitHub API v4ã§ä½¿ç¨ããã¦ããæ¹æ³ã§ãä¸å®æéã®ã¯ã¨ãªã®ã³ã¹ããç´¯ç©ãããããå¤ãè¶
ããå ´åå©ç¨è
ã«å¶éã課ãã¾ããGitHubã®ããã«ãµã¼ããã¼ãã£ã«APIãå
¬éããå ´åã¯æå¹ãªæ段ããããã¾ããã
GitHubã®rate limitä»æ§ã¯resource-limitationsãåç §ãã ããã
Introspection query
Intorospectionã¨ã¯GraphQLã¹ãã¼ãæ å ±ãåå¾ããããã®ä»æ§ã§ããµã¼ãã¼ããç´æ¥ä½¿ç¨å¯è½ãªQueryãªã©ã®æ å ±ãå¾ããã¾ãã
éçºæã¯Queryã®ä½æãã³ã¼ãçæã«å©ç¨ãããã¨ãã§ãã¦é常ã«ä¾¿å©ãªä»æ§ã§ããããããæªæã®ããã¦ã¼ã¶ã¼ãç°¡åã«å¦çã®å 容ãç¥ããã¨ãã§ãã¦ãã¾ããããæ»æã®ã¹ããä¸ãããã¨ã«ãªã£ã¦ãã¾ãã¾ãã
ApolloServerãå©ç¨ãã¦ããå ´åã¯introspection
ãªãã·ã§ã³ã§è¨å®å¯è½ã§ãNODE_ENV
ãproduction
ã®å ´åããã©ã«ãã§ãªãã«ãªãã¾ãã
Field suggestion
ApolloServerãå©ç¨ãã¦ããå ´åãã¹ãã¼ãã«åå¨ããªãqueryããã£ã¼ã«ãã解決ãããã¨ããæã«ããããããã¦xxxã®ãã¨ãæãã¦ãã¾ãã?ãã¨ããããææ¡ãã¦ããããã¨ãããã¾ãã
{
"error": {
"errors": [
{
"message": "Cannot query field \\"producte\\" on type \\"Query\\". Did you mean \\"product\\"?",
"locations": [
{
"line": 2,
"column": 3
}
]
}
]
}
}
éçºæã¯ä¾¿å©ãªæ©è½ã§ããããããIntorospectionã¨åãããã«èå¼±æ§ã«ç¹ããã¾ãã
ApolloServerãå©ç¨ãã¦ããå ´åã¯ä¸è¨ã®ããã«formatError
ã«æ¹ä¿®ãå ãããã¨ã§å¶å¾¡ã§ãã¾ãã
const server = new ApolloServer({
...
formatError: (err) => {
if (isProduction && error instanceof ValidationError) {
return new ValidationError('Invalid query');
}
return err;
},
});
ã¹ã¿ãã¯ãã¬ã¼ã¹
ãããGraphQLã«éã£ã話ã§ã¯ããã¾ããããApolloServerãªã©ã®å®è£ ã§ã¯ãã¨ã©ã¼ã®å 容ã«ã¹ã¿ãã¯ãã¬ã¼ã¹ãå«ã¾ãã¾ããã¹ã¿ãã¯ãã¬ã¼ã¹ã«ã¯ãã£ã¬ã¯ããªæ§é ãªã©ãå«ã¾ãããããæå³ããç§å¿æ å ±ãå ¬éãã¦ãã¾ãå¯è½æ§ãããã¾ãã
{
"error": {
"errors": [
{
"message": "Cannot query field \\"booke\\" on type \\"Query\\". Did you mean \\"books\\"?",
"locations": [
{
"line": 2,
"column": 3
}
],
"extensions": {
"code": "GRAPHQL_VALIDATION_FAILED",
"exception": {
"stacktrace": [
"GraphQLError: Cannot query field \\"booke\\" on type \\"Query\\". Did you mean \\"books\\"?",
" at Object.Field (/path_to_app/node_modules/graphql/validation/rules/FieldsOnCorrectTypeRule.js:48:31)",
" at Object.enter (/path_to_app/node_modules/graphql/language/visitor.js:323:29)",
" at Object.enter (/path_to_app/node_modules/graphql/utilities/TypeInfo.js:370:25)",
" at visit (/path_to_app/node_modules/graphql/language/visitor.js:243:26)",
" at Object.validate (/path_to_app/node_modules/graphql/validation/validate.js:69:24)",
" at validate (/path_to_app/node_modules/apollo-server-core/dist/requestPipeline.js:233:34)",
" at Object.<anonymous> (/path_to_app/node_modules/apollo-server-core/dist/requestPipeline.js:119:42)",
" at Generator.next (<anonymous>)",
" at fulfilled (/path_to_app/node_modules/apollo-server-core/dist/requestPipeline.js:5:58)",
" at processTicksAndRejections (node:internal/process/task_queues:96:5)"
]
}
}
}
]
}
}
ApolloServerã®å ´ådebug
ãªãã·ã§ã³ã«ãã£ã¦è¨å®å¯è½ã§ãNODE_ENV
ãproduction
ã®å ´åã¯ããã©ã«ãã§ãªãã«ãªãã¾ãã
ã¹ãã¼ãè¨è¨
æå¾ã«GraphQLãéç¨ããä¸ã§æãéè¦ãªã¹ãã¼ãè¨è¨ã«é¢ãã¦ã§ããããã¾ã§èª¬æãã¦ããå 容ã¯æ¯è¼çéçºãé²ãã¤ã¤æ¹ä¿®å¯è½ãªå 容ã§ããããã¹ãã¼ãè¨è¨ã¯ä¸åº¦ãµã¼ãã¹ããªãªã¼ã¹ãã¦ãã¾ãã¨å¤§ããå¤æ´ãå ãããã¨ã¯äºææ§ã®è¦³ç¹ããå°é£ã«ãªãã¾ããäºããã¼ã ã§æ¹éãããåããã¦ããéçºãããã¨ããããããã¾ãã
ãªã½ã¼ã¹ã®ã°ã©ãæ§é ã®ä¿å®
GraphQLã®è¨è¨ã¯ãªã½ã¼ã¹ãä¸å¿ã«ã°ã©ããæ§ç¯ãã¦ãããã¨ãåæã¨ãªã£ã¦ããã®ã§ããã®ã°ã©ãæ§é ãç ´ç¶»ããã¨é端ã«æ±ãã«ãããã®ã«ãªãã¾ãã
ã°ã©ãæ§é ãæ´çããã¦ããªãå ´åãã¨ããç»é¢ã§ã¯æ¬æ¥ã¯åä¸ã®ãªã¯ã¨ã¹ãã§æ¸ãã¯ãããç´åã«2ã¤ã®ãªã¯ã¨ã¹ããçºçããããã¦ã¼ã¹ã±ã¼ã¹ãã¨ã«Queryã追å ããç¾½ç®ã«ãªãã¾ãã
ã¡ã«ã«ãªShopsã®åå詳細ã®å®è£ ãèãã¾ããåå詳細ã§ã¯ååãã®ãã®ã¨ããã®ååãã©ã®ã·ã§ããããåºåãããã®ãã¨ããæ å ±ã表示ãã¾ãã
query ($id: String!) {
product(id: $id) {
id
shopId
...
}
}
query ($shopId: String!) {
shop(id: $shopId) {
id
...
}
}
ååãä¸ä»¶åå¾ããQuery productã¨ã·ã§ãããä¸ä»¶åå¾ããQuery shopãæ¢ã«åå¨ããå ´åãååã¯ã·ã§ããã¸ã®é¢é£ãæã¤ãããshopId
ãå©ç¨ãã¦2åã«åãã¦åå¾ãããã¨ãã§ãã¾ãããããããã¯ã°ã©ãæ§é ã®æ§ç¯ãã¯ã©ã¤ã¢ã³ãå´ã«æããããã¨ã«ãªãä¸ãããã©ã¼ãã³ã¹é¢ã§ãæé©åããã¦ãã¾ãããã°ã©ãã®æ§é ä¸ãååããç´æ¥ã·ã§ãããåç
§ããã¹ãã§ãã
# product queryã®å®è¡çµæã«shopãå«ãã
query ($id: String!) {
product(id: $id) {
id
shop {
id
}
}
}
RESTã®å ´åãé¢é£ãããªã½ã¼ã¹ãå ¨ã¦çµæã«å«ãã¦ãã¾ãã¨ãã¬ã¹ãã³ã¹ãè¥å¤§åãã¦ãã¾ãããã常ã«ãã®è¨è¨ã«ããã®ã¯é£ããé¢ãããã¾ãããããGraphQLã§ã¯ã¯ã©ã¤ã¢ã³ããè¦æ±ããªãéããµã¼ãã¼å´ã§å¦çãè¡ããã¨ã¯ç¡ãã®ã§ããã£ã¼ã«ãã«ç´æ¥åãè¾¼ããã¨ãåºæ¥ã¾ãã
ä»åç´¹ä»ããä¾ã¯å ¸åçãªGraphQLã®ã¢ã³ããã¿ã¼ã³ã§ãããã¦ã¼ã¹ã±ã¼ã¹ãã¨ã«æ©è½ã追å ãã¦ããã¨ã¤ãããªã½ã¼ã¹ã®ã°ã©ãæ§é ã®é¢ä¿æ§ãããã¾ãã«ãªããã®ã§ããæ°ããªãªãã¸ã§ã¯ãããã£ã¼ã«ãã追å ããéã¯ãå ¨ä½ã®ã°ã©ãæ§é ã«ç«ã¡è¿ã£ã¦æ éã«è°è«ããå¿ è¦ãããã¾ãã
ID表ç¾
facebookãéçºãã¦ããRelayããã¸ã§ã¯ãã§ã¯GraphQL Server Specificationãå ¬éããã¦ãã¾ããGraphQLã®ã¹ãã¼ãè¨è¨ã®ãã¹ããã©ã¯ãã£ã¹ãã¾ã¨ãããã®ã§ãRelayãã¯ã©ã¤ã¢ã³ãã¨ãã¦ä½¿ç¨ããªãå ´åã§ãããã®ä»æ§ãåèã«å®è£ ããã¦ãããµã¼ãã¼ãã¯ã©ã¤ã¢ã³ãã¯æ°å¤ãããã®ã§åèã«ããã¨ä¸æãããã±ã¼ã¹ãå¤ãã§ãã
Global Object Specificationããã®ä¸ã¤ã§ãID表ç¾ã®ãã¹ããã©ã¯ãã£ã¹ã¨ãã¦ç´¹ä»ããã¦ãã¾ãã
REST APIãªã©ãå©ç¨ãã¦ããå ´åãURLããã¼ã¹ã«HTTPãã£ãã·ã¥ãå©ç¨ãããã¨ãã§ãã¾ãããGraphQLã®å ´åã¯URLãªã©ã®èå¥åãåå¨ããªãããããã£ãã·ã¥ã®ããã®èå¥åã¨ãã¦ããªã½ã¼ã¹ã«å¯¾ãã¦ã°ãã¼ãã«ãªIDãå¿
è¦ã«ãªãã¾ããä¾ãã°Productã®IDãp001
ã®å ´åProduct:001
ã®ãããªIDãçæãããªã½ã¼ã¹ã横æãã¦ã¦ãã¼ã¯ãªç¶æ
ãæ
ä¿ãã¾ã(base64ãªã©ã§å度ã¨ã³ã³ã¼ãããã®ãä¸è¬çã§ã)ã
ã¾ãIDãããªã½ã¼ã¹ã®ç¨®å¥ãå¤å¥ã§ããã¨æ¬¡ã®ããã«åä¸ã®queryã§ãªã½ã¼ã¹ã®åå¾ãã§ããã®ã§ååå¾ã®å®è£ ãç°¡åã«ãªãã¾ãã
query {
node(id: âProduct:p001â) {
id
... on Product {
name
}
}
}
}
ã¨ã¯ãããBFF+micro serviceã®ãããªæ§æã ã¨data sourceå´ã§ã°ãã¼ãã«ã«ã¦ãã¼ã¯ãªIDãå²ãæ¯ãä»çµã¿ãæä¾ããã®ã¯é£ããããããã¾ããããã®å ´åGraphQLã¬ã¤ã¤ã¼ã§IDãåæ§ç¯ãã¾ãã
ã¡ã«ã«ãªShopsã§ã¯æ®å¿µãªããæ¬ä»æ§ã«æ²¿ã£ã¦ä½ããã¦ãã¾ããããApolloClientã§ã¯ããã©ã«ãã§__typeName
ã¨ãªã½ã¼ã¹ã®IDãçµã¿åããããã®ããã£ãã·ã¥ãã¼ã¨ãã¦å©ç¨ãã¾ããããã¯æ£ã«ãåè¿°ããä»æ§ã§ã代æ¿ææ³ã¨ãã¦ä¸ãããã¦ããæ¹æ³ã§ãã
ã¡ãªã¿ã«ä»¥ä¸ã®ãã¨ããèå¥åãæããªããã ã®ãªãã¸ã§ã¯ããªã©ãè¿å´ããå ´åã¯æ³¨æãå¿ è¦ã§ããIDãªã©ã®èå¥åããªãå ´åãã¯ã©ã¤ã¢ã³ãå´ãæã¤ãªã³ã¡ã¢ãªã®ãã£ãã·ã¥ã¯æ´æ°ãããªãããã§ãã
ãã¼ã¸ãã¼ã·ã§ã³è¡¨ç¾
GraphQLã«éããããã¼ã¸ãã¼ã·ã§ã³ã®ã¤ã³ã¿ã¼ãã§ã¼ã¹ãæãã¦ãããã¨ã¯å¼ã³åºãå´ã®ä»çµã¿ãåå©ç¨ããä¸ã§éè¦ã§ãããApolloãRelayãªã©ã®ã¯ã©ã¤ã¢ã³ããå©ç¨ãã¦ããå ´åã¯ããã以ä¸ã®æå³ãæã¡ã¾ãã
GraphQLã§ã¯ãã£ãã·ã¥ãå¹ççã«å©ç¨ããããã®ãã¹ããã©ã¯ãã£ã¹ã¨ãã¦ãConnectionsã¨ããã«ã¼ã½ã«ãã¼ã¹ã®ãã¼ã¸ãã¼ã·ã§ã³ãç´¹ä»ããã¦ãã¾ãã
ä¾ãã°productã®ãªã¹ããåå¾ããå ´å次ã®ãããªã¯ã¨ãªã«ãªãã¾ãã
{
products(first: 100, after: "cursorid") {
edges {
cursor
node {
id
}
}
pageInfo {
hasNextPage
startCursor
endCursor
}
}
}
Connectionsã¯æ¬¡ã®è¦ç´ ã§æ§æããã¾ãã
- ãµã¤ãºãæå®ããå¼æ°
first
ãã«ã¼ã½ã«ä½ç½®ãæå®ããå¼æ°after
- åãã¼ãã«å¯¾å¿ãã
cursor
ãæã¤edges
- ãã¼ã¸æ
å ±ãæã¤
pageInfo
åãã¼ããcursor
ãæã£ã¦ãã¾ãããããã¯ã¯ã©ã¤ã¢ã³ãã§ãã£ãã·ã¥ãå¹ççã«ä½¿ç¨ããããã®ãã®ã§ããä¾ãã°ããç»é¢ã§10件ãã¼ã¿ãåå¾ããéãç»é¢ã§åããªã½ã¼ã¹ã5件åå¾ããå ´åããµã¼ãã¼ããå度ãã¼ã¿ãåå¾ããã¨ãæ£ããpageInfo
ãåæ§æãããã¨ãã§ãã¾ãã
ã¡ã«ã«ãªShopsã®ã¦ã¼ã¹ã±ã¼ã¹ã§ã¯pageInfo
ã®æã¤startCursor
ã¨endCursor
ã§ååãªã®ã§ãåedge
ã®cursor
ã¯çç¥ãã¦ãã¾ãã
ã¾ããRelayããã¼ã¹ã«ããä»æ§ã§ãããApolloClientãå©ç¨ãã¦ããå ´åã次ã®ããã«typePolicy
ãè¨å®ãããã¨ã§ãç°¡åã«ãªã³ã¡ã¢ãªã®ãã£ãã·ã¥ãæ´æ°ã§ãã¾ãã
import { relayStylePagination } from '@apollo/client/utilities';
const client = new ApolloClient({
cache: new InMemoryCache({
Query: {
fields: {
products: relayStylePagination({...}),
},
},
}),
});
Mutationã®ã¬ã¹ãã³ã¹è¡¨ç¾
ããã¯RESTã®å ´åãåããã¨ãè¨ããã®ã§ãããã¯ã©ã¤ã¢ã³ãã«ãªã½ã¼ã¹ã®å¤æ´å 容ãéç¥ããããã«ãMutationã®è¿å´å¤ã¯å¤åãèµ·ãããªã½ã¼ã¹ããã®ã¾ã¾è¿ãã®ãæã¾ããã§ãã
åé ã§ã説æããããã«ãApolloãRelayãªã©ã®ã¯ã©ã¤ã¢ã³ãã§ã¯GraphQLã®ã¬ã¹ãã³ã¹ã¯æ£è¦åãã¦ãªã³ã¡ã¢ãªä¸ã«ãã£ãã·ã¥ããããããã¬ã¹ãã³ã¹ã«ãªã½ã¼ã¹ãè¿å´ãããã¨ã§èªåçã«ãã£ãã·ã¥ãæ´æ°ããã¾ããã¬ã¹ãã³ã¹ã«ãªã½ã¼ã¹ãå«ã¾ããªãå ´åãã¯ã©ã¤ã¢ã³ãã¯å度ãªã½ã¼ã¹ãæå®ãã¦åå¾ããå¿ è¦ãããã¾ããã¾ããããã§åé ã®IDä»æ§ã«å¾ã£ã¦queryãæ§ç¯ããã¦ããã¨ååå¾ãç°¡åã«ãªãã¾ãã
ã¡ã«ã«ãªShopsã§ã¯æ®å¿µãªããå ¨ã¦ã®Mutationã§å¯¾å¿ã§ãã¦ããããã§ã¯ããã¾ããããå¾ã ã«æ¹ä¿®ãé²ãã¦ãã¾ãã
ã¹ã¿ã¤ã«ã¬ã¤ãã®çå®
ããã¾ã§ããã¤ãã¹ãã¼ãè¨è¨ã®ãã³ãã¨ãªãæ å ±ãç´¹ä»ãã¦ãã¾ããããå½åè¦åãã¹ãã¼ãè¨èªã¨ãã¦ã®æ©è½ã®å©ç¨æ¹éãªã©ãè°è«ã®å¯¾è±¡ã¯æ°å¤ãããã¾ããã¬ãã¥ã¼ã§å¤æ´ãè°è«ããã®ã¯ãã¡ããã§ãããã¹ã¿ã¤ã«ã¬ã¤ãã®ãããªåå°ãããã¨ã¬ãã¥ã¼ã®ã³ã¹ããæ¸ãããªã³ãã¼ãã£ã³ã°ãã¼ã«ã¨ãã¦ãå½¹ç«ã¡ã¾ãã
次ã®ãªã³ã¯ã¯ãæ®æ®µç§ãåèã«ãã¦ããAPIãã¬ã¤ãããæç²ãããã®ã§ããã©ããæ±ç¨çã§å®ç¨çãªæ å ±ãããããããã¾ã¨ã¾ã£ã¦ãã¾ããæåã¯ä¸è¨ã®ãããªæ¢åã®ã¬ã¤ããåèã«é²ããã®ãè¯ãããããã¾ããã
ã¡ã«ã«ãªShopsã§ã¯ä»ã®æãããã£ãéç¨ã¯ã§ãã¦ãã¾ããããå ¨å¡ã½ããã¦ã§ã¢ã¨ã³ã¸ãã¢ã¨ããä½å¶ãæ¯æ´ããããã«ããã¹ã¿ã¤ã«ã¬ã¤ãçå®ã«åãçµãã§è¡ãããã¨æã£ã¦ãã¾ãã
ãããã«
ãã®è¨äºã§ã¯GraphQLãéç¨ããä¸ã§äºãèæ ®ããã»ããè¯ãåºæ¬çãªé ç®ãããã¤ãç´¹ä»ãã¾ãããããã¾ã§ç´¹ä»ãã¦ããå å®¹å ¨ã¦ã対å¿ããå¿ è¦ã¯ããã¾ããããæ°ãã«GraphQLã®éç¨ãå§ããéãè°è«ã®åèã«ãã¦é ããã¨å¹¸ãã§ãã
ã¡ã«ã«ãªShopsã§ãã¾ã ã¾ã 課é¡ã¯å±±ç©ã¿ã§ããä¸ç·ã«èª²é¡ã解決ãã¦ãããã¡ã³ãã¼ã大åéä¸ãªã®ã§ã¡ã«ã«ãªShopsã®éçºãã½ã¦ã¾ã¦ã«èå³ãæã£ãæ¹ãããã°ãã²ãå¿åãå¾ ã¡ãã¦ãã¾ãã
詳ããã¯ä»¥ä¸ã®ãã¼ã¸ãã覧ãã ããã
- Software Engineer
- Software Engineer, Site Reliability
- Software Engineer, Machine Learning
- Software Engineer, QA Test
- Software Engineer (Internship) â Mercari Group ï¼â»æ°åæ¡ç¨ã«å¿åããã«ã¯ã¾ãã¤ã³ã¿ã¼ã³ã¸ã®åå ããé¡ããã¦ãã¾ããï¼
ã¾ãã«ã¸ã¥ã¢ã«ã«é°å²æ°ã話ã ãèãã¦ã¿ãããã¨ãã£ãæ¹ã大æè¿ã§ãããã¡ãã®ç³ãè¾¼ã¿ãã©ã¼ã ãããã²ãé£çµ¡ãã ããã