Simplify Database Schemas with GraphQL, Prisma, and Generative Types


Posted on Mon, Jul 17, 2023 database apollo graphql typescript mongodb prisma

Simplifying database schema management can be a challenging task, especially when dealing with complex data structures.

However, with the help of GraphQL, Apollo, Prisma, and generative types, this process can be made much easier. Prisma is a next-generation ORM that simplifies working with SQL in JavaScript and TypeScript, while Apollo provides a powerful and flexible way to build GraphQL APIs.

By using generative types, developers can create type-safe database access and simplify the management of their database schema. In this article, we will explore how these technologies can be used together to simplify database schema management and improve the efficiency of your development process.

Understanding the Basics πŸ”¨

In this section, we will take a closer look at the four tools that we will be using to simplify database schema management: GraphQL, Apollo, Prisma, and Generative Types. We will discuss what each tool does, how they work together, and why they are a good fit for this task. Let's get started!

GraphQL and Apollo πŸš€

GraphQL is a query language and server-side runtime that makes it easier for frontend developers to perform data fetching. It is designed to make APIs fast, flexible, and developer-friendly. As an alternative to REST, GraphQL allows us to pull data from multiple data sources from only a single API call. Here are some key features of GraphQL :

Apollo Server is a lightweight and flexible server that makes it easy to build scalable and performant GraphQL APIs. It supports a wide range of data sources, including databases, REST APIs, and other services, making it easy to integrate with existing systems. The Apollo Client makes it easy to query and mutate data, and provides advanced features like caching, optimistic UI, and real-time updates.

Prisma for Database Management 🌩️

Prisma is an open-source ORM that simplifies and enhances database interactions in software development projects. It provides a modern data access tool that allows developers to write safe and efficient database queries in a way that is easy to understand and manage.

Prisma replaces traditional ORMs and simplifies database workflows by providing a custom and auto-generated client that enables type-safe database access. It also provides a declarative data modeling and migration system that simplifies the process of making changes to the database schema. Prisma is a powerful tool that can be used to build GraphQL, REST, gRPC APIs, and a lot more.

Generating Types in GraphQL πŸ›ž

The power of generative types in GraphQL lies in its ability to provide a strongly-typed system that ensures data consistency and enables efficient development workflows. Here are some key points to understand about the power of generative types in GraphQL :

1. Type Safety ⛑️

GraphQL uses a type system to define the available data for each type and field in a schema. This allows developers to have clear and explicit expectations about the shape and structure of the data they are working with

2. Automatic Type Generation πŸ€–

Tools like the GraphQL Code Generator library can automatically generate types based on the GraphQL schema. This saves developers from manually writing and maintaining types, reducing the chances of errors and inconsistencies

3. Efficient Development βš™οΈ

With generative types, developers can leverage the benefits of static typing, such as autocompletion, type checking, and IDE support. This leads to faster development cycles and fewer runtime errors

4. Schema Consistency πŸ™‚

Generative types ensure that the schema and the corresponding types are always in sync. Any changes made to the schema will automatically trigger updates to the generated types, maintaining consistency throughout the development process.

Step-by-Step Guide to Simplifying Your Database Schema

➑️

Using GraphQL, Apollo, Prisma, and Generative Types

This section will walk you through the steps of setting up a GraphQL API with Apollo, Prisma, and Generative Types. By the end of this guide, you will have a simple and easy-to-manage database schema that can be used to power your GraphQL API.

Without further ado, let’s get started ! πŸš€

1 : Setting up our new project

Create a new directory for your project and initialize a new npm project.

mkdir graphql-project
cd graphql-project
npm init -y

2 : Install the required packages

Install the necessary packages for our project :

npm install apollo-server graphql dotenv @prisma/client prisma typescript mongodb --save

3 : Set up TypeScript Configurations

Create a tsconfig.json file in the project's root directory. This code configures TypeScript to use ES2020, CommonJS modules, strict mode, and outputs compiled files to a "dist" folder. :

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "CommonJS",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true}
}

4 : Set up Apollo Server

Create a new file index.ts in the project's root directory. This file will serve as the entry point to our application. The code below sets up an Apollo Server with type definitions and resolvers, then starts the server and logs the URL.

// index.ts
import { ApolloServer } from "apollo-server";
import { typeDefs } from "./src/schema";
import { resolvers } from "./src/resolvers";

const server = new ApolloServer({
  typeDefs,
  resolvers,
});

server.listen().then(({ url }) => {
  console.log(`πŸš€ Server ready at ${url}`);
});

5 : Defining GraphQL Schemas

Create a new directory src in the project's root directory. Inside the src directory, create two new files: schema.ts and resolvers.ts.

schema.ts : This code defines a GraphQL schema with a "User" type having fields "id," "name," and "email," and a "users" query.

//schema.ts
import { gql } from "apollo-server";

export const typeDefs = gql`
  type User {
    id: ID!
    name: String!
    email: String!
  }

  type Query {
    users: [User!]!
  }
`;

resolvers.ts : The code snippet below defines GraphQL resolvers using PrismaClient to fetch "users" data from the database, handling errors and returning results.

// resolvers.ts
import { PrismaClient } from "@prisma/client";

const prisma = new PrismaClient();

export const resolvers = {
  Query: {
    users: async () => {
      try {
        const users = await prisma.user.findMany();
        if (users.length > 0) {
          // Data exists in the database
          return users;
        } else {
          // Data is empty in the database
          console.log("No data found in the database.");
          return [];
        }
      } catch (error) {
        console.error("Error fetching users from the database:", error);
        throw new Error("Failed to fetch users from the database.");
      }
    },
  },
};

6 : Prisma Initialization

Initialize Prisma in your project :

# init prisma project with npx
npx prisma init

This command will create a new prisma folder in your project directory.

7 : Configure Database Connection

In the prisma folder, you'll find a file called schema.prisma. Update it to use MongoDB Atlas as the database provider :

// prisma/schema.prisma
datasource db {
  provider = "mongodb"
  url      = env("MONGODB_URI")
}

model User {
  id    String  @id @default(auto()) @map("_id") @db.ObjectId
  email String  @unique
  name  String?
}

8 : Generate The Prisma Client

Run the following command to generate the Prisma Client :

# generate the client!
npx prisma generate

9 : Store credentials in .env

Create a .env file in the project's root directory and store your MongoDB Atlas connection string :

MONGODB_URI=mongodb+srv://<USERNAME>:<PASSWORD>@<YOUR_CLUSTER_URL>/<DATABASE_NAME>?retryWrites=true&w=majority

Replace <USERNAME>, <PASSWORD>, <YOUR_CLUSTER_URL>, and <DATABASE_NAME> with your actual MongoDB Atlas credentials.

10 : Add build script

Open the package.json file in the project's root directory and add the build script :

{
  "name": "graphql-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "ts-node index.ts",
    "build": "tsc"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@prisma/client": "^5.0.0",
    "apollo-server": "^3.12.0",
    "dotenv": "^16.3.1",
    "graphql": "^16.7.1",
    "mongodb": "^5.7.0",
    "prisma": "^5.0.0",
    "typescript": "^5.1.6"
  }
}

11 : Build and Run

Build the TypeScript files and start the server :

# build with tsc
npm run build

# run the server
node dist/index.js

If your setup is successful, you should see a webpage similar to the shown below if you click the port shown after running node dist/index.js

12 : Test the GraphQL API

Open your browser and navigate to http://localhost:4000/.

You can now test your GraphQL API using Apollo Server's built-in GraphQL Playground πŸ”₯

You’re seeing the user’s data because I already have that in my database. You’ll get an empty array if you’re querying an empty database.

You can download the project file below :

graphql-project (3).zip53255.8KB

Conclusion

Combining GraphQL, Apollo, Prisma, and generating TypeScript types can simplify database schema management. This approach can help create an easy-to-manage database schema that powers your GraphQL API.

By using the strongly-typed nature of a GraphQL schema, type generation libraries can automatically generate TypeScript types based on that schema.

You can use these generated types to ensure type safety for the inputs and results of your GraphQL operations. Prisma is an ORM that can be used inside the GraphQL resolvers of your Apollo Server to query your database.