Apollo-server-express with Typescript Unknown route

APM Agent language and version:
"elastic-apm-node": "^3.14.0",

with

"apollo-server-express": "^2.0.4",
"graphql": "^15.5.0",

I managed to get the integration working between the graphql server and APM. But it seems that all transaction are set to unknown route

It seems to me that it's not shimming the graphql or appollo library maybe?

package.json

      "name": "proxy",
      "private": true,
      "version": "0.0.1",
      "scripts": {
        "start": "nodemon --exec \"ncc run\" src/index.ts",
        "build": "ncc build src/index.ts",
        "build:watch": "yarn build --watch",
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "dependencies": {
        "@grpc/grpc-js": "^1.2.12",
        "apollo-server-express": "^2.0.4",
        "elastic-apm-node": "^3.14.0",
        "google-protobuf": "^3.15.6",
        "graphql": "^15.5.0",
        "grpc": "^1.24.6"
      },
      "devDependencies": {
        "@types/node": "^14.14.35",
        "@vercel/ncc": "^0.27.0",
        "nodemon": "^2.0.7",
        "ts-node-dev": "^1.1.6",
        "typescript": "^4.2.3"
      }
    }

index.ts

    import { ApolloServer, gql } from "apollo-server-express";
    import {createServer} from "http";
    import {resolvers, typeDefs} from "./schema";

    const PORT = process.env.PORT || 3030;


    const app = Express()
    const apollo = new ApolloServer({ typeDefs, resolvers });

    app.use(apollo.getMiddleware({cors: false, path: '/'}))

    const httpServer = createServer(app)

    httpServer.listen({ port: PORT }, () =>
      console.log(
        `🚀 Server ready at http://localhost:${PORT}${apollo.graphqlPath}. Using APM ${process.env.ELASTIC_APM_SERVER_URL} GRPC ${process.env.BACKEND_ADDR}`
      )
    );

I run it with:

    $ ELASTIC_APM_LOG_LEVEL=trace ELASTIC_APM_LOGGER=false node -r elastic-apm-node/start dist/index.js

log output

      pid: 68802,
      ppid: 68581,
      arch: 'x64',
      platform: 'darwin',
      node: 'v15.13.0',
      agent: '3.12.1',
      ancestors: [
        '/Users/username/dev/project/tracing-experiment/proxy/node_modules/elastic-apm-node/index.js',
        '/Users/username/dev/project/tracing-experiment/proxy/node_modules/elastic-apm-node/start.js',
        null,
        [length]: 3
      ],
      startTrace: [
        'at Agent.start (/Users/username/dev/project/tracing-experiment/proxy/node_modules/elastic-apm-node/lib/agent.js:179:11)',
        'at Object.<anonymous> (/Users/username/dev/project/tracing-experiment/proxy/node_modules/elastic-apm-node/start.js:3:32)',
        'at Module._compile (node:internal/modules/cjs/loader:1092:14)',
        'at Object.Module._extensions..js (node:internal/modules/cjs/loader:1121:10)',
        'at Module.load (node:internal/modules/cjs/loader:972:32)',
        'at Function.Module._load (node:internal/modules/cjs/loader:813:14)',
        'at Module.require (node:internal/modules/cjs/loader:996:19)',
        'at Module._preloadModules (node:internal/modules/cjs/loader:1251:12)',
        'at loadPreloadModules (node:internal/bootstrap/pre_execution:455:5)',
        'at prepareMainThreadExecution (node:internal/bootstrap/pre_execution:74:3)',
        [length]: 10
      ],
      main: undefined,
      dependencies: undefined,
      conf: {
        ignoreUrlStr: [ [length]: 0 ],
        ignoreUrlRegExp: [ [length]: 0 ],
        ignoreUserAgentStr: [ [length]: 0 ],
        ignoreUserAgentRegExp: [ [length]: 0 ],
        transactionIgnoreUrlRegExp: [ [length]: 0 ],
        sanitizeFieldNamesRegExp: [
          /^password$/i { [lastIndex]: 0 },
          /^passwd$/i { [lastIndex]: 0 },
          /^pwd$/i { [lastIndex]: 0 },
          /^secret$/i { [lastIndex]: 0 },
          /^.*key$/i { [lastIndex]: 0 },
          /^.*token.*$/i { [lastIndex]: 0 },
          /^.*session.*$/i { [lastIndex]: 0 },
          /^.*credit.*$/i { [lastIndex]: 0 },
          /^.*card.*$/i { [lastIndex]: 0 },
          /^authorization$/i { [lastIndex]: 0 },
          /^set\x2dcookie$/i { [lastIndex]: 0 },
          /^pw$/i { [lastIndex]: 0 },
          /^pass$/i { [lastIndex]: 0 },
          /^connect\.sid$/i { [lastIndex]: 0 },
          [length]: 14
        ],
        abortedErrorThreshold: 25,
        active: true,
        apiRequestSize: 786432,
        apiRequestTime: 10,
        asyncHooks: true,
        breakdownMetrics: true,
        captureBody: 'off',
        captureErrorLogStackTraces: 'messages',
        captureExceptions: true,
        captureHeaders: true,
        captureSpanStackTraces: true,
        centralConfig: true,
        cloudProvider: 'auto',
        disableInstrumentations: [ [length]: 0 ],
        environment: 'development',
        errorMessageMaxLength: 2048,
        errorOnAbortedRequests: false,
        filterHttpHeaders: true,
        instrument: true,
        instrumentIncomingHTTPRequests: true,
        logLevel: 'trace',
        logUncaughtExceptions: false,
        metricsInterval: 30,
        metricsLimit: 1000,
        sanitizeFieldNames: [
          'password',   'passwd',
          'pwd',        'secret',
          '*key',       '*token*',
          '*session*',  '*credit*',
          '*card*',     'authorization',
          'set-cookie', 'pw',
          'pass',       'connect.sid',
          [length]: 14
        ],
        serverTimeout: 30,
        sourceLinesErrorAppFrames: 5,
        sourceLinesErrorLibraryFrames: 5,
        sourceLinesSpanAppFrames: 0,
        sourceLinesSpanLibraryFrames: 0,
        stackTraceLimit: 50,
        transactionIgnoreUrls: [ [length]: 0 ],
        transactionMaxSpans: 500,
        transactionSampleRate: 1,
        useElasticTraceparentHeader: true,
        usePathAsTransactionName: false,
        verifyServerCert: true,
        serverUrl: 'http://localhost:8200',
        serviceName: 'proxy',
        serviceVersion: '0.0.1',
        serverHost: 'localhost:8200',
        serverPort: 8200
      }
    }
    adding hook to Node.js module loader
    shimming http@15.13.0 module
    shimming http.Server.prototype.emit function
    shimming http.ServerResponse.prototype.writeHead function
    shimming http.request function
    shimming http.get function
    shimming https@15.13.0 module
    shimming https.Server.prototype.emit function
    shimming https.request function
    shimming https.get function
    shimming http2@15.13.0 module
    shimming http2.createServer function
    shimming http2.connect function
    no active transaction found - cannot build new span
    intercepted call to http.request { id: null }
    no active transaction found - cannot build new span
    intercepted call to http.request { id: null }
    no active transaction found - cannot build new span
    intercepted call to http.request { id: null }
    🚀 Server ready at http://localhost:3030/. Using APM http://localhost:8200 GRPC localhost:5000
    no cloud metadata servers responded
    intercepted request event call to http.Server.prototype.emit for /graphql

I found the problem, it turns out ncc does not work well with how APM is integrated

Nice work! The self help desk is the best help desk :slight_smile:

@yulrizka was the problem that ncc compiles your typescript into a single file? (which means that the elastic-apm-node module is no longer the first one loaded). That's the most common problem we see with APM (across vendors) and TypeScript. At a high level you can often fix this with some extra build/compile steps that add the elastic-apm-node require/start to the top of your single file.

If this wasn't the problem would you be able to share more about what you mean when you say

not work well with how APM is integrated

We're always interested in hearing how the agent behaves with other Node.js tooling and doing what we can to make that experience smoother.

It sounds like you're on your way to a solution here, but if you need more help (or if I've misread what you're saying) please don't hesitate to keep this conversation going.

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.