<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sebastian hilton</title>
    <description>The latest articles on DEV Community by Sebastian hilton (@bastianhilton).</description>
    <link>https://dev.to/bastianhilton</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F198133%2Fc4c7579c-9044-41c7-b4ea-f56a9563fddb.png</url>
      <title>DEV Community: Sebastian hilton</title>
      <link>https://dev.to/bastianhilton</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bastianhilton"/>
    <language>en</language>
    <item>
      <title>Building a scalable and extensive Graphql Server</title>
      <dc:creator>Sebastian hilton</dc:creator>
      <pubDate>Sun, 08 Jan 2023 02:39:56 +0000</pubDate>
      <link>https://dev.to/bastianhilton/building-a-scalable-and-extensive-graphql-server-1gkn</link>
      <guid>https://dev.to/bastianhilton/building-a-scalable-and-extensive-graphql-server-1gkn</guid>
      <description>&lt;p&gt;Finding the perfect open source graphql server is not an easy task. When I was searching for a solution that combined the great Prisma ORM and Graphql-Yoga I wasn't able to find a workable solution anywhere. So I made my own and extended it with Typescript support, Envelop extensions, SOFA api support, and file upload support.&lt;/p&gt;

&lt;p&gt;The first thing is what are all of these technologies that we are including in our server:&lt;/p&gt;

&lt;p&gt;First up is, &lt;strong&gt;What is Graphql&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;GraphQL is an open-source data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data. GraphQL was developed internally by Facebook in 2012 before being publicly released in 2015.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Prisma?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Prisma unlocks a new level of developer experience when working with databases thanks to its intuitive data model, automated migrations, type-safety &amp;amp; auto-completion.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;What is Graphql-yoga?&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
GraphQL Yoga is a batteries-included cross-platform GraphQL over HTTP spec-compliant GraphQL server powered by Envelop and GraphQL Tools that runs anywhere; focused on easy setup, performance and great developer experience.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;What is Typegraphql?&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
Modern framework for GraphQL API in Node.js, used to provide typescript and graphql to Prisma.&lt;/p&gt;

&lt;p&gt;Lets get started by creating a directory for our project:&lt;/p&gt;

&lt;p&gt;open your command prompt or terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir apiserver

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then change to that directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd apiserver

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;_Make sure you install Nodejs to your operating system before continuing.&lt;br&gt;
_&lt;br&gt;
now lets create a npm project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(-y is used to quickly except all default values)&lt;/p&gt;

&lt;p&gt;create a file in your directory called index.ts&lt;/p&gt;

&lt;p&gt;Now to install all that is required lets install everything at once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install ts-node type-graphql @prisma/client@4.6.1 prisma graphql-yoga @graphql-yoga/plugin-sofa typescript graphql @types/node @envelop/core class-validator reflect-metadata typegraphql-prisma 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A bit of what we just installed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ts-node so we can run .ts (typescript) files&lt;/li&gt;
&lt;li&gt;type-graphql to provide typescript and graphql to prisma&lt;/li&gt;
&lt;li&gt;typescript to add typescript to our server&lt;/li&gt;
&lt;li&gt;&lt;p&gt;@prisma/client and prisma to install the Prisma ORM to allow connecting to our databases. We are using version 4.6.1 because thats the latest version that typegraphql supports.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;graphql-yoga/node is a package that is the base of our server&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;@graphql-yoga/plugin-sofa is to generate Rest APIs from our graphql schema&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;graphql to allow graphql support in our project&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;@types/node for node.js typescript support&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;@envelop/core to allow envelop plugins in our server&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;typegraphql-prisma for type-graphql and prisma integration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;class-validator is a decorator-based property validation for classes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;reflect-metadata is required to make the type reflection work&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;now lets create a typescript configuration project within our server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx tsc --init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in our root directory (the main directory of our server) and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx prisma init --datasource-provider sqlite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;that initiated a Prisma directory with the schema.prisma file inside.&lt;/p&gt;

&lt;p&gt;this schema file shows&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7pp9f8rxofef60cfrtug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7pp9f8rxofef60cfrtug.png" alt="Image description" width="673" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;lets update our schema.prisma file to add typegraphql support:&lt;/p&gt;

&lt;p&gt;in your file you should already have&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;datasource db {
  provider = "sqlite"
  url      = "file:./dev.db"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets migrate our database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx prisma migrate dev --name init

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other options can be found here for Prisma supported databases and how to use them: &lt;a href="https://www.prisma.io/docs/concepts/database-connectors/sqlite" rel="noopener noreferrer"&gt;Prisma Databases&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;now lets add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;generator typegraphql {
  provider           = "typegraphql-prisma"
  output             = "../prisma/generated/type-graphql"
  emitTranspiledCode = "true"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What the above does is add a generator that will generate a generated folder with our CRUD functionality, resolvers, and other code to make our schema type-safe and ready for our graphql server.&lt;/p&gt;

&lt;p&gt;more on this here: &lt;a href="https://typegraphql.com/docs/prisma.html" rel="noopener noreferrer"&gt;Typegraphql-Prisma&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now lets create a dev.db file in our root folder for our sqlite database.&lt;/p&gt;

&lt;p&gt;now lets add our data to our database by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx prisma db push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our two models are in our dev.db file. Great we are ready to generate the data so that it is available for our server to detect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx prisma generate

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the prisma side of things are completed, we need to work on our server.&lt;/p&gt;

&lt;p&gt;in our index.ts file lets add some things, first we need to import all of our packages that we downloaded earlier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require("reflect-metadata");

import { buildSchema } from "type-graphql";
import * as path from "path";
import { PrismaClient } from "@prisma/client";
import { useParserCache } from '@envelop/parser-cache';
import { useValidationCache } from '@envelop/validation-cache';

import { createYoga } from 'graphql-yoga';
import { createServer } from 'node:http';
import { createFetch } from '@whatwg-node/fetch';
import { useGraphQlJit } from '@envelop/graphql-jit';
import { resolvers } from "../prisma/generated/type-graphql";
import { useSentry } from '@envelop/sentry';
import { useSofaWithSwaggerUI } from '@graphql-yoga/plugin-sofa'
import { APIGatewayEvent, APIGatewayProxyResult, Context } from 'aws-lambda'

import '@sentry/tracing';
//import { ApolloGateway } from '@apollo/gateway'
//import { useApolloFederation } from '@envelop/apollo-federation'
import fastify, { FastifyRequest, FastifyReply } from 'fastify'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So with my above imports you can see i added reflect-metadata at the top, in order for typegraphql to work properly it must have that package at the top. &lt;/p&gt;

&lt;p&gt;There are also a lot of other packages that are here that we didn't discuss earlier. For instance all the @envelop plugins are optional and added because of things like error reporting, logging, and running in a node environment such as Fastify and Node which you can substitute for other frameworks if you wish. &lt;/p&gt;

&lt;p&gt;here are the supported frameworks: &lt;a href="https://the-guild.dev/graphql/yoga-server/docs" rel="noopener noreferrer"&gt;Graphql-yoga Integrations&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and then go to integrations on the left to select your nodejs framework of choice. &lt;/p&gt;

&lt;p&gt;Now lets add the rest of our code, the following will be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fastify will be our framework of choice with logging support&lt;/li&gt;
&lt;li&gt;&lt;p&gt;cors module added because if added to another nodejs application, this server will be available at localhost:4000/graphql and should be accessible via a graphql-client queries and mutations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We will create our main server function to hold our graphql server, prisma connection, schema, and resolvers, envelop plugins, logging functionality, cors, plugins, sofa with swagger support, and file upload functionality within the createFetch function.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then we will call our server using the&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const server = createServer(yoga) 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will then add our routing for our graphql server with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.route({})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other personal features I added was AWS Lambda functionality for serverless deployment and commented out is support for Apollo Federation which allows multiple graphql endpoints in our app.&lt;/p&gt;

&lt;p&gt;Apollo Federation can be commented out if you have a graphql url to add or else it will cause the server to crash as it can't detect the invalid url.&lt;/p&gt;

&lt;p&gt;Lets add some additional functionality for our server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @envelop/graphql-jit @envelop/parser-cache @envelop/sentry @envelop/validation-cache @sentry/core @sentry/tracing @types/graphql @types/graphql-fields @whatwg-node/fetch aws-lambda fastify cors graphql-fields graphql-scalars graphql-ws tslib @types/aws-lambda
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While most of the above you might not need to build your own server, these are additional functionality to help with error reporting (Sentry), graphql support for typescript (Typegraphql), our nodejs framework (fastify), graphql-subscriptions (graphql-ws), serverless deployment (aws-lambda), and validation &amp;amp; caching support.&lt;/p&gt;

&lt;p&gt;The final code is below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require("reflect-metadata");

import { buildSchema } from "type-graphql";
import * as path from "path";
import { PrismaClient } from "@prisma/client";
import { useParserCache } from '@envelop/parser-cache';
import { useValidationCache } from '@envelop/validation-cache';

import { createYoga } from 'graphql-yoga';
import { createServer } from 'node:http';
import { createFetch } from '@whatwg-node/fetch';
import { useGraphQlJit } from '@envelop/graphql-jit';
import { resolvers } from "../prisma/generated/type-graphql";
import { useSentry } from '@envelop/sentry';
import { useSofaWithSwaggerUI } from '@graphql-yoga/plugin-sofa'
import { APIGatewayEvent, APIGatewayProxyResult, Context } from 'aws-lambda'

import '@sentry/tracing';
//import { ApolloGateway } from '@apollo/gateway'
//import { useApolloFederation } from '@envelop/apollo-federation'
import fastify, { FastifyRequest, FastifyReply } from 'fastify'

// This is the fastify instance you have created
const app = fastify({
  logger: true
})

// Setting cors and logging capabilities

var cors = require('cors')

app.options('*', cors())

// Initialize the gateway
/* const gateway = new ApolloGateway({
  serviceList: [
    { name: 'First', url: process.env.GRAPHQL_ENV },
    //{ name: 'products', url: 'http://localhost:4002' }
  ]
}) */

// Pulling our Graphql Resolvers from Type-graphql &amp;amp; Prisma generation

async function main() {
  const schema = await buildSchema({
    resolvers,
    emitSchemaFile: path.resolve(__dirname, "./generated-schema.graphql"),
    validate: false,
  });

  // Make sure all services are loaded
  // await gateway.load()

  // Connect to Prisma

  const prisma = new PrismaClient();
  await prisma.$connect();

  // Graphql Server main function 

  const yoga = createYoga &amp;lt; {
    req: FastifyRequest
    reply: FastifyReply
    event: APIGatewayEvent
    lambdaContext: Context
  } &amp;gt; ({
    // Integrate Fastify logger
    logging: {
      debug: (...args) =&amp;gt; args.forEach((arg) =&amp;gt; app.log.debug(arg)),
      info: (...args) =&amp;gt; args.forEach((arg) =&amp;gt; app.log.info(arg)),
      warn: (...args) =&amp;gt; args.forEach((arg) =&amp;gt; app.log.warn(arg)),
      error: (...args) =&amp;gt; args.forEach((arg) =&amp;gt; app.log.error(arg))
    },
    schema,
    //context: contextCreator,
    batching: true,
    cors: {
      origin: '*',
      credentials: true,
    },
    context: ({}) =&amp;gt; ({
      prisma,
    }),
    plugins: [
      useParserCache({}),
      useValidationCache({}),
      useGraphQlJit({}),
      useSentry({
        includeRawResult: false, // set to `true` in order to include the execution result in the metadata collected
        includeResolverArgs: false, // set to `true` in order to include the args passed to resolvers
        includeExecuteVariables: false, // set to `true` in order to include the operation variables values
      }),
      /* useApolloFederation({
         gateway
       }) */
       useSofaWithSwaggerUI({
        basePath: '/rest',
        swaggerUIEndpoint: '/swagger',
        servers: [
          {
            url: '/', // Specify Server's URL.
            description: 'Development server'
          }
        ],
        info: {
          title: 'Example API',
          version: '1.0.0'
        }
      })
    ],
    fetchAPI: createFetch({
      // We prefer `node-fetch` over `undici` and current unstable Node's implementation
      useNodeFetch: true,
      formDataLimits: {
        // Maximum allowed file size (in bytes)
        fileSize: 1000000,
        // Maximum allowed number of files
        files: 10,
        // Maximum allowed size of content (operations, variables etc...)
        fieldSize: 1000000,
        // Maximum allowed header size for form data
        headerSize: 1000000
      }
    })
  });

  const server = createServer(yoga)

  app.route({
    url: '/graphql',
    method: ['GET', 'POST', 'OPTIONS'],
    handler: async (req, reply) =&amp;gt; {
      // Second parameter adds Fastify's `req` and `reply` to the GraphQL Context
      const response = await yoga.handleNodeRequest(req, {
        req,
        reply
      })
      response.headers.forEach((value, key) =&amp;gt; {
        reply.header(key, value)
      })

      reply.status(response.status)

      reply.send(response.body)

      return reply
    }

  })

  // Serverless Lambda feature

  async function handler(
    event: APIGatewayEvent,
    lambdaContext: Context
  ): Promise&amp;lt;APIGatewayProxyResult&amp;gt; {
    const url = new URL(event.path, 'http://localhost')
    if (event.queryStringParameters != null) {
      for (const name in event.queryStringParameters) {
        const value = event.queryStringParameters[name]
        if (value != null) {
          url.searchParams.set(name, value)
        }
      }
    }

    const response = await yoga.fetch(
      url,
      {
        method: event.httpMethod,
        headers: event.headers as HeadersInit,
        body: event.body
          ? Buffer.from(event.body, event.isBase64Encoded ? 'base64' : 'utf8')
          : undefined
      },
      {
        event,
        lambdaContext
      }
    )

    const responseHeaders: Record&amp;lt;string, string&amp;gt; = {}

    response.headers.forEach((value, name) =&amp;gt; {
      responseHeaders[name] = value
    })

    return {
      statusCode: response.status,
      headers: responseHeaders,
      body: await response.text(),
      isBase64Encoded: false
    }
  }

  server.listen(4000, () =&amp;gt; {
    console.info('Server is running on http://localhost:4000/graphql')
  })
}

main().catch((e) =&amp;gt; {
  console.error(e);
  process.exit(1);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can see the gist here: &lt;a href="https://gist.github.com/bastianhilton/31d8f516035a53c0e5575b80232d8934" rel="noopener noreferrer"&gt;https://gist.github.com/bastianhilton/31d8f516035a53c0e5575b80232d8934&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations you have created a graphql server that supports Prisma, Typescript, envelop plugins, automatically generated rest apis with SOFA, Apollow Federation, and serverless deployment with Lambda.&lt;/p&gt;

&lt;p&gt;Lets run the server from our root directory with ts-node index.ts&lt;/p&gt;

&lt;p&gt;the end result will look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvs7qgj9rvfysuzyxr3s3.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvs7qgj9rvfysuzyxr3s3.jpeg" alt="Image description" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can plug and play this server into any nodejs environment to instantly add graphql for your databases.&lt;/p&gt;

&lt;p&gt;With prisma you also have support for SQLite, SQL, SQL Server, Supabase, Postgresql, MySQL, MongoDB, CockroachDB, and Planetscale.&lt;/p&gt;

</description>
      <category>gratitude</category>
    </item>
  </channel>
</rss>
