Открояване на серия от данни:

  • Apollo Server се предлага като пакет Node. Можем да го използваме, за да създадем сървър, който да приема GraphQL заявки.

В тази статия ще разгледаме как да добавяме грешки към сървър Express Apollo.

Обработка на грешки

Apollo Server има колекция от предварително дефинирани грешки, включително AuthenticationError, ForbiddenError, UserInputError и ApolloError.

Тези грешки правят нашето приложение лесно за отстраняване на грешки, като предоставят информация за нашата грешка.

Например, можем да върнем грешки, както следва:

const express = require('express');
const { ApolloServer, gql, SchemaDirectiveVisitor } = require('apollo-server-express');
const fs = require('fs');
const typeDefs = gql`
  type Query {
    readError: String
  }
`;
const resolvers = {
  Query: {
    readError: (parent, args, context) => {
      fs.readFileSync('/does/not/exist');
    },
  },
};
const app = express();
const server = new ApolloServer({
  typeDefs, resolvers,
});
server.applyMiddleware({ app });
app.listen(3000, () => console.log('server started'));

В кода по-горе се опитваме да прочетем файл, който не съществува в нашия резолвер.

Така че, когато направим заявката:

{
  readError
}

Ще получим:

{
  "errors": [
    {
      "message": "ENOENT: no such file or directory, open '/does/not/exist'",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "readError"
      ],
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR",
        "exception": {
          "errno": -2,
          "code": "ENOENT",
          "syscall": "open",
          "path": "/does/not/exist",
          "stacktrace": [
            "Error: ENOENT: no such file or directory, open '/does/not/exist'",
            "    at Object.fs.openSync (fs.js:660:18)",
            "    at Object.fs.readFileSync (fs.js:565:33)",
            "    at readError (/home/runner/index.js:14:10)",
            "    at field.resolve (/home/runner/node_modules/graphql-extensions/dist/index.js:133:26)",
            "    at resolveFieldValueOrError (/home/runner/node_modules/graphql/execution/execute.js:467:18)",
            "    at resolveField (/home/runner/node_modules/graphql/execution/execute.js:434:16)",
            "    at executeFields (/home/runner/node_modules/graphql/execution/execute.js:275:18)",
            "    at executeOperation (/home/runner/node_modules/graphql/execution/execute.js:219:122)",
            "    at executeImpl (/home/runner/node_modules/graphql/execution/execute.js:104:14)",
            "    at Object.execute (/home/runner/node_modules/graphql/execution/execute.js:64:35)"
          ]
        }
      }
    }
  ],
  "data": {
    "readError": null
  }
}

Можем да видим следата на стека къде е проблемът.

Кодове

Експортираните грешки на Apollo Server посочват четим от човека низ в полето code на extensions, който позволява на клиента да извършва коригиращи действия.

Също така ни позволява да категоризираме грешките.

Например, ако имаме:

const express = require('express');
const { ApolloServer, gql, AuthenticationError } = require('apollo-server-express');
const typeDefs = gql`
  type Query {
    authenticationError: String
  }
`;
const resolvers = {
  Query: {
    authenticationError: (parent, args, context) => {
      throw new AuthenticationError('not authenticated');
    },
  },
};
const app = express();
const server = new ApolloServer({
  typeDefs, resolvers,
});
server.applyMiddleware({ app });
app.listen(3000, () => console.log('server started'));

Тогава получаваме:

{
  "errors": [
    {
      "message": "not authenticated",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "authenticationError"
      ],
      "extensions": {
        "code": "UNAUTHENTICATED",
        "exception": {
          "stacktrace": [
            "AuthenticationError: not authenticated",
            "    at authenticationError (/home/runner/index.js:13:13)",
            "    at field.resolve (/home/runner/node_modules/graphql-extensions/dist/index.js:133:26)",
            "    at resolveFieldValueOrError (/home/runner/node_modules/graphql/execution/execute.js:467:18)",
            "    at resolveField (/home/runner/node_modules/graphql/execution/execute.js:434:16)",
            "    at executeFields (/home/runner/node_modules/graphql/execution/execute.js:275:18)",
            "    at executeOperation (/home/runner/node_modules/graphql/execution/execute.js:219:122)",
            "    at executeImpl (/home/runner/node_modules/graphql/execution/execute.js:104:14)",
            "    at Object.execute (/home/runner/node_modules/graphql/execution/execute.js:64:35)",
            "    at /home/runner/node_modules/apollo-server-core/dist/requestPipeline.js:246:46",
            "    at Generator.next (<anonymous>)"
          ]
        }
      }
    }
  ],
  "data": {
    "authenticationError": null
  }
}

Виждаме, че получаваме AuthenticationError, защото можем да видим, че:

"code": "UNAUTHENTICATED"

е в нашата грешка JSON.

Увеличаване на подробностите за грешката

Можем да добавим повече подробности за грешките от предоставените по подразбиране.

Например можем да напишем:

const express = require('express');
const {
  ApolloServer,
  UserInputError,
  gql,
} = require('apollo-server-express');
const typeDefs = gql`
  type Mutation {
    userInputError(input: String): String
  }
  type Query {
    hello: String
  }
`;
const resolvers = {
  Query: {
    hello: () => 'Hello'
  },
  Mutation: {
    userInputError: (parent, args, context, info) => {
      if (args.input !== 'expected') {
        throw new UserInputError('Form Arguments invalid', {
          invalidArgs: Object.keys(args),
        });
      }
    },
  },
};
const app = express();
const server = new ApolloServer({
  typeDefs, resolvers,
});
server.applyMiddleware({ app });
app.listen(3000, () => console.log('server started'));

В кода по-горе имаме:

throw new UserInputError('Form Arguments invalid', {
          invalidArgs: Object.keys(args),
        });

в нашата мутация, за да добавим свойството invalidArgs към нашия JSON за грешка и стойностите args, които изпратихме.

Следователно, когато правим заявка, както следва:

mutation {
  userInputError(input: "foo")
}

Получаваме:

{
  "errors": [
    {
      "message": "Form Arguments invalid",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "userInputError"
      ],
      "extensions": {
        "invalidArgs": [
          "input"
        ],
        "code": "BAD_USER_INPUT",
        "exception": {
          "invalidArgs": [
            "input"
          ],
          "stacktrace": [
            "UserInputError: Form Arguments invalid",
            "    at userInputError (/home/runner/index.js:25:15)",
            "    at field.resolve (/home/runner/node_modules/graphql-extensions/dist/index.js:133:26)",
            "    at resolveFieldValueOrError (/home/runner/node_modules/graphql/execution/execute.js:467:18)",
            "    at resolveField (/home/runner/node_modules/graphql/execution/execute.js:434:16)",
            "    at /home/runner/node_modules/graphql/execution/execute.js:244:18",
            "    at /home/runner/node_modules/graphql/jsutils/promiseReduce.js:23:10",
            "    at Array.reduce (<anonymous>)",
            "    at promiseReduce (/home/runner/node_modules/graphql/jsutils/promiseReduce.js:20:17)",
            "    at executeFieldsSerially (/home/runner/node_modules/graphql/execution/execute.js:241:37)",
            "    at executeOperation (/home/runner/node_modules/graphql/execution/execute.js:219:55)"
          ]
        }
      }
    }
  ],
  "data": {
    "userInputError": null
  }
}

Частта:

"invalidArgs": [
            "input"
          ]

не е включено по подразбиране, ние го добавихме чрез предаване на втори аргумент към конструктора UserInputError.

Грешки при пренаписване

Можем да добавим функция rewriteError, която да връща само някои грешки, така че да се докладват само някои.

Например можем да напишем:

const server = new ApolloServer({
  typeDefs, resolvers, engine: {
    rewriteError(err) {
      if (err instanceof AuthenticationError) {
        return null;
      }
      return err;
    }
  },
});

за да избегнете изпращането на AuthenticationError s до Apollo Graph Manager.

Можем също така да върнем модифициран обект err, така че да можем да редактираме някои данни, които не искаме да регистрираме.

Заключение

Apollo Server идва с някои вградени грешки. Можем да ги хвърлим, когато трябва да ги регистрираме в Apollo Graph Manager и да покажем информацията за грешка в отговора.

Можем също така да добавим наши собствени данни към грешките, като ги предадем във втория аргумент на конструкторите.