Back to Prisma1

03 Build Graphql Servers With Prisma GO G201

docs/1.31/get-started/03-build-graphql-servers-with-prisma-GO-g201.mdx

1.34.128.6 KB
Original Source

import QueryChooser from 'components/Markdown/QueryChooser' import Collapse from 'components/Markdown/Collapse' import Warning from 'components/Markdown/Warning' import Info from 'components/Markdown/Info'

export const meta = { title: 'Build an App', gettingStartedTitle: 'GraphQL API', position: 3, gettingStartedOrder: 1, nextText: 'Congratulations! 🚀 You made it through the quickstart tutorial and learned how to use Prisma and the Prisma client to build a GraphQL server.', technology: 'go', technologyOrder: 4, articleGroup: 'Build an App', }

Goals

On this page, you will learn how to:

  • Define a GraphQL schema for your GraphQL server
  • Implement resolver functions using the Prisma client
  • Use the GraphQL Playground to try out your GraphQL API

Configure project

You're using gqlgen as a GraphQL server library for this project. gqlgen is also used to generated code based on your GraphQL schema to help you build type-safe GraphQL servers.

Create a new directory and add the gqlgen code generation script to it:

bash
mkdir scripts
touch scripts/gqlgen.go

Now add the following code to gqlgen.go:

go
// +build ignore

package main

import "github.com/99designs/gqlgen/cmd"

func main() {
	cmd.Execute()
}

Finally, update the dependencies of your project:

bash
dep ensure -update

Define GraphQL API & create project sceleton

Create the GraphQL schema

Every GraphQL API is based on a GraphQL schema that specifies all API operations and data structures. The schema is a contract between client and server.

Create your GraphQL schema definition file inside a new directory called server:

bash
mkdir server
touch servers/schema.graphql

Now add the following type definitions to the schema file:

graphql
type Query {
  publishedPosts: [Post!]!
  post(postId: ID!): Post
  postsByUser(userId: ID!): [Post!]!
}

type Mutation {
  createUser(name: String!): User
  createDraft(title: String!, userId: ID!): Post
  publish(postId: ID!): Post
}

type User {
  id: ID!
  email: String
  name: String!
  posts: [Post!]!
}

type Post {
  id: ID!
  title: String!
  published: Boolean!
  author: User
}

Configure gqlen

Next, you'll use gqlgen to scaffold the resolvers for your GraphQL server. You first need to create the config file gqlgen.yml that's used as foundation for the code generation process:

bash
touch gqlgen.yml

Now add the following configuration to it:

yml
schema: server/schema.graphql
exec:
  filename: server/generated.go
models:
  Post:
    model: hello-world/prisma-client.Post
  User:
    model: hello-world/prisma-client.User
resolver:
  # Goal: copy&paste from generated file
  filename: tmp/resolver.go
  type: Resolver

Finally, use the gqlgen code generation script to create the foundation for your GraphQL server:

bash
go run scripts/gqlgen.go

The files in the generated tmp directory contain empty GraphQL resolvers that you need to implement. First, copy the file into a more appropriate location:

bash
cp ./tmp/resolver.go ./server/

When making changes to your GraphQL schema in the future, you need to run $ go run scripts/gqlgen.go again. This will override the files in tmp so you can copy and paste the new resolvers into their permanent location in servers/resolvers.go.

Implement resolver functions

Now add proper resolver implementations to resolver.go, replace the entire contents of the file with the following:

go
package main

import (
	"context"
	"hello-world/generated/prisma-client"
)

type Resolver struct {
	Prisma *prisma.Client
}

func (r *Resolver) Mutation() MutationResolver {
	return &mutationResolver{r}
}
func (r *Resolver) Post() PostResolver {
	return &postResolver{r}
}
func (r *Resolver) Query() QueryResolver {
	return &queryResolver{r}
}
func (r *Resolver) User() UserResolver {
	return &userResolver{r}
}

type mutationResolver struct{ *Resolver }

func (r *mutationResolver) CreateUser(ctx context.Context, name string) (*prisma.User, error) {
	return r.Prisma.CreateUser(prisma.UserCreateInput{
		Name: name,
	}).Exec(ctx)
}
func (r *mutationResolver) CreateDraft(ctx context.Context, title string, userId string) (*prisma.Post, error) {
	return r.Prisma.CreatePost(prisma.PostCreateInput{
		Title: title,
		Author: &prisma.UserCreateOneWithoutPostsInput{
			Connect: &prisma.UserWhereUniqueInput{ID: &userId},
		},
	}).Exec(ctx)
}
func (r *mutationResolver) Publish(ctx context.Context, postId string) (*prisma.Post, error) {
	published := true
	return r.Prisma.UpdatePost(prisma.PostUpdateParams{
		Where: prisma.PostWhereUniqueInput{ID: &postId},
		Data:  prisma.PostUpdateInput{Published: &published},
	}).Exec(ctx)
}

type postResolver struct{ *Resolver }

func (r *postResolver) Author(ctx context.Context, obj *prisma.Post) (*prisma.User, error) {
	return r.Prisma.Post(prisma.PostWhereUniqueInput{ID: &obj.ID}).Author().Exec(ctx)
}

type queryResolver struct{ *Resolver }

func (r *queryResolver) PublishedPosts(ctx context.Context) ([]prisma.Post, error) {
	published := true
	return r.Prisma.Posts(&prisma.PostsParams{
		Where: &prisma.PostWhereInput{Published: &published},
	}).Exec(ctx)
}
func (r *queryResolver) Post(ctx context.Context, postId string) (*prisma.Post, error) {
	return r.Prisma.Post(prisma.PostWhereUniqueInput{ID: &postId}).Exec(ctx)
}
func (r *queryResolver) PostsByUser(ctx context.Context, userId string) ([]prisma.Post, error) {
	return r.Prisma.Posts(&prisma.PostsParams{
		Where: &prisma.PostWhereInput{
			Author: &prisma.UserWhereInput{
				ID: &userId,
			}},
	}).Exec(ctx)
}

type userResolver struct{ *Resolver }

func (r *userResolver) Posts(ctx context.Context, obj *prisma.User) ([]prisma.Post, error) {
	return r.Prisma.User(prisma.UserWhereUniqueInput{ID: &obj.ID}).Posts(nil).Exec(ctx)
}

Each resolver invokes a method on the Prisma client instance which is called Prisma and attached to the incoming resolver object of r.

Configure your GraphQL server

There are a few configuration steps left until you can start the server.

Update the package name at the top of generated.go from package server to:

go
package main

Next, move the index.go script you've been using on the previous pages into the server directory:

bash
mv index.go ./server

Now replace the contents of index.go with the following:

go
package main

import (
	"log"
	"net/http"
	"os"

	"hello-world/generated/prisma-client"

	"github.com/99designs/gqlgen/handler"
)

const defaultPort = "4000"

func main() {
	port := os.Getenv("PORT")
	if len(port) == 0 {
		port = defaultPort
	}

	client := prisma.New(nil)
	resolver := Resolver{
		Prisma: client,
	}

	http.Handle("/", handler.Playground("GraphQL Playground", "/query"))
	http.Handle("/query", handler.GraphQL(NewExecutableSchema(Config{Resolvers: &resolver})))

	log.Printf("Server is running on http://localhost:%s", port)
	err := http.ListenAndServe(":"+port, nil)
	if err != nil {
		log.Fatal(err)
	}
}

Start the GraphQL server

To start the GraphQL server, run the following command:

bash
go run ./server

Explore the GraphQL API in a Playground

The GraphQL API of your application layer now exposes the six operations defined in schema.graphql.

To test these operations, navigate your browser to http://localhost:4000 where a GraphQL Playground is running.

<Collapse title="Learn more about the GraphQL Playground">

A GraphQL Playground is an interactive GraphQL IDE that lets you explore the operations of GraphQL API. You can click the green SCHEMA-button at the right edge of the Playground window to view the auto-generated documentation for your GraphQL API.

</Collapse>

Here are a few sample queries and mutations you can send to explore the API.

<QueryChooser titles={["Create a new User", "Create new draft", "Publish a Post", "Fetch published Posts"]}>

graphql
mutation {
  createUser(name: "Bob") {
    id
  }
}
graphql
mutation {
  createDraft(
    title: "GraphQL is great"
    userId: "__USER_ID__"
  ) {
    id
    published
    author {
      id
    }
  }
}
graphql
mutation {
  publish(id: "__POST_ID__") {
    id
    title
    published
  }
}
graphql
query {
  publishedPosts {
    id
    title
    author {
      id
      name
    }
  }
}
</QueryChooser>

In some snippets, you need to replace the __USER__ID__ or __POST_ID__ placeholder with the ID of an actual user.