Running NodeJS

NodeJS Server Code Overview

🙋 Need help? Ask an expert now!

Server Code Flow

Now that we have it actually running, let's again read and understand what's in the src/server.js. We should use VSC on the host for that.

startServers() is the entry point. The callback construction allows executing a function after the server's start - in our case, we don't have anything to do, so we don't pass a callback to startServers. Obviously, this piece came from some old bootstrap project - as we said we're committed to not use callbacks in our code. Well, we're not using it.

Connecting to MongoDB

function startGraphQLServer starts with the connection to MongoDB. We provide a set of common parameters to mongoose, some of which are workarounds addressing current state of the product, e.g., mongoose.set('useCreateIndex', true) is set by itself vs. in the part where we set autoReconnect: true and useNewUrlParser: true. As you go through version upgrades, this can be streamlined at some point in the future. The promiseDb section is taken from some specific sample and may need to be adjusted in the future as well. This is a normal process of following changes and upgrades in 3rd party npm packages.

Configuring Express Middleware

const graphQLApp = express(); - we are using the express framework. Our GraphQL stuff runs via express-graphql. express is a very popular choice when building NodeJS-based web apps, if not the default, so we're on board with it.

express utilizes the concept of use middleware and next(): the HTTP request travels through all the functions added to the flow by use statements in the code, in the order they are added. When each function completes successfully, it runs next(), otherwise, without next() called, the process is assumed ended, and express returns whatever HTTP response has been put together by that point. Commonly, if the entire flow has not been completed before ending - it means something failed, e.g., the authentication, and an HTTP Error Code is sent back, e.g., 401 Unauthorized.

Via graphQLApp.use, we're adding:

  • /healthcheck - when the request comes to the /healthcheck path, e.g., http://localhost:8080/healthcheck, the nice little express-healthcheck package handles it: "yes, we're up and running". This simple feature is extremely important when deploying production instances, as Load Balancers and container orchestration frameworks are designed to ping running servers continuously (run healthcheck), and if no response is received - the server is considered dead and gets replaced

  • cors (Cross-Origin Resource Sharing) - to enable multi-server handling of clients' requests. E.g., say the client's browser is displaying a https://exlskills.com page, so the application running in the browser is considered to be originated from the exlskills.com domain. If the client application needs to exchange some data with the GraphQL server running on gql-api.exlskills.com domain (exlskills.com and gql-api.exlskills.com are different domains), it is supposed to check with the server if the server agrees accepting the request first and only then send/pull the data. The administering of the CORS logic is done by the client's application (usually, at the API level): e.g., it may allow GET requests to always go through and only validate the cross-origin compliance for requests that modify data. CORS is sparsely documented and confusing. The definition of the origin consists of the domain as well as the port and protocol. The setup is done on the target server - which origins the server accepts requests from, by the request's method (e.g., GET, POST, PUT) and any additional applicable conditions. However, the target server by itself is not enforcing the CORS logic, just passes the info to the client app to do so. So, it feels like CORS is designed to protect the client (or User), not the server: if the client side is tempered with, the server has no apparent way of blocking incoming requests on its end.
    As a common strategy, for requests that fall under the CORS compliance umbrella, the client's application sends a preflight request to the server first. The server replies with a dataset of origins and request types as per its CORS configuration. The client's application checks if what it needs to send is allowed per the dataset - and either sends the actual request to the target server or doesn't. In the latter case, an error may or may not be generated in the browser applications. Without a CORS error, the developer is often left clueless why the request was not sent.
    Details of GraphQL API CORS implementation are not scope here. As you watch the browser traffic, you may see two requests going out for each mutation: a preflight one following by the data. Queries appear to be flowing with no regards for CORS validation. As you know, either queries or mutations may be coded to change server data, so the role of CORS in doing any good is quite doubtful. No wonder, most tutorials recommend setting CORS to a * - allow any.

  • cookieParser - reads cookies from the request

  • /graph - this is the path that gets processed by the GraphQL engine. First, middleware.getViewer is executed, and if it succeeds, then graphQLHTTP kicks in. Notice middleware.loginRequired commented out - we'll discuss the access security next.

GraphQL configuration in graphQLHTTPOpts is pretty straightforward: enable the graphiql browser tester, use Schema imported from schema.js.

context: request.gqlviewer is the code that tells GraphQL to use request.gqlviewer element as the context. We've seen viewer passed as the third argument to resolver functions when we reviewed the GraphQL sample query structure. Technically, the 3rd argument is the context, and the configuration above defines that its value comes from the gqlviewer element in the request. gqlviewer is generated in middleware.getViewer, so all the ends finally meet.

With the express middleware flow configured, we can start it listening on the GRAPHQL_PORT:

graphQLServer = graphQLApp.listen(GRAPHQL_PORT, () => {

An amazingly simple way configuring and launching an HTTP server process - NodeJS at its best!

So, what server do we run here? Is it NodeJS, or HTTP, or express, or GraphQL? Does it really matter? You can call it NodeJS when identifying the backbone technology, or GraphQL when taking about the app component in the stack. HTTP or express will not say much to anyone if used describing this application.


Next, a quick look at JWT and the viewer concept, as those are pretty important in the overall architecture once a server like this goes live

Edit Me on GitHub!