Criando um Chat com

Go + GraphQL

Meetup GO BH - Jun, 2020

RODRIGO BRITO

/rodrigo-brito

@RodrigoFBrito

Go na Academia

Go na Academia

RAID: Refactoring-aware and Intelligent Diffs

https://github.com/rodrigo-brito/refactoring-aware-diff

Missão do Dia

Layout by Zeno Rocha - Codepen

GraphQL

Dê ao seu cliente o poder de pedir exatamente o que ele precisa

REST

https://api.example/v1/user/{ID}

Exemplo: api.example/v1/user/123

Parâmetros

Operação

Resposta Fixa

{

       id: 123,

       name: "Fulano da Silva",

       email: "fulano@fulano.com.br",

       phone: "(31) 1234-4567",

       city: "Belo Horizonte"

}

GraphQL

Exemplo: api.example/graphql

{

       user(id: 123) {

            name

       }

}

Parâmetros

Operação

O usuário pode fazer várias consultas em uma requisição

Resposta Dinâmica

{

       name: "Fulano da Silva"

}

Schema

type Message {

     author: String!
     content: String!
}

type Query {
     messages(limit: Int!): [Message!]
}

type Mutation {
     sendMessage(input: MessageInput!): Message
}

Interface / Assinaturas da API

https://graphql.org/learn/schema/

Publish–Subscribe Pattern

type Subscription {
     newMessage(chat: ID!): Message
}

Mutation

Subscription

Topic

Schema First (Geração de código)

Exemplo: github.com/99designs/gqlgen

type Message {
  author: string!
  content: string!
}

query {
  messages(limit: Int!): [Message!]
}
type Message struct {
  Author string
  Content  string
}

type QueryRoot interface {
  Messages(limit int) ([]*Message, error)
}

type Query struct { ... }

func (s Query) Messages(limit int) ([]*Message, error) {
	// your logic here...
}

schema.graphql

GQLGen Config

Documentação: https://gqlgen.com/config/

schema:
  - schema.graphql

exec:
  filename: schema_gen.go
  package: graphql

model:
  filename: model/models_gen.go
  package: model

.gqlgen.yaml

$ go get -u  github.com/99designs/gqlgen

$ gqlgen generate

Exemplos de Implementação - Query

type Query struct {
	MessageService service.Message
}

func (q Query) Messages(ctx context.Context, limit int) ([]*model.Message, error) {
	return q.MessageService.Fetch(ctx, limit)
}
type QueryResolver interface {
	Messages(ctx context.Context, limit int) ([]*model.Message, error)
}

Interface Requerida

Implementação

Exemplos de Implementação - Mutation

type Mutation struct {
	MessageService service.Message
}

func (m Mutation) SendMessage(ctx context.Context, input model.MessageInput) (*model.Message, error) {
	message := model.Message{
		AuthorID:  input.AuthorID,
		Author:    input.Author,
		Content:   input.Message,
		CreatedAt: time.Now(),
	}

	err := m.MessageService.Save(ctx, message)
	if err != nil {
		return nil, ErrServiceUnavailable
	}

	return &message, nil
}
type MutationResolver interface {
	SendMessage(ctx context.Context, input model.MessageInput) (*model.Message, error)
}

Interface Requerida

Implementação

Exemplos de Implementação - Subscription

func (s Subscription) Messages(ctx context.Context) (<-chan *model.Message, error) {
	subscription := s.PubSub.Sub(topicName)
	messages := make(chan *model.Message)

	go func() {
		for {
			select {
            
			case <-ctx.Done():
				s.PubSub.Unsub(subscription, topicName)
                
			case message := <-subscription:
				if message != nil {
					messages <- message.(*model.Message)
				}
			}
		}
	}()

	return messages, nil
}
type SubscriptionResolver interface {
	Messages(ctx context.Context) (<-chan *model.Message, error)
}

Interface Requerida

Implementação

const QUERY_MESSAGES = gql`
  query Messages {
    messages(limit: 100) {
      authorID
      author
      content
    }
  }
`;

function Messages() {
  const { loading, error, data } = useQuery<Query>(QUERY_MESSAGES);

  return (
    <div>
      {data.messages.map((message, index) => (
        <p key={index}>
          {message.content}
        </p>
      ))}
    </div>
  div
}

Query

const MUTATION_SEND_MESSAGE = gql`
  mutation($input: MessageInput!) {
    sendMessage(input: $input) {
      authorID
      author
      content
    }
  }
`;

const [sendMessage] = useMutation<Mutation, MutationSendMessageArgs>(
    MUTATION_SEND_MESSAGE
);

const onSubmit = (e: FormEvent) => {
  sendMessage({
    variables: {
      input: {
        authorID: uuid,
        author: name,
        message: message,
      },
    },
  });
}

Mutation

const SUBSCRIPTION_MESSAGES = gql`
  subscription {
    messages {
      authorID
      author
      content
    }
  }
`;

useSubscription<Subscription>(SUBSCRIPTION_MESSAGES, {
  onSubscriptionData: (data) => {
    const result = data.subscriptionData.data.messages;

    setConversation([
      ...conversation, { author: result.author, content: result.content },
    ]);
  },
});

Subscription

Criando um Chat Go + GraphQL

By Rodrigo Brito

Criando um Chat Go + GraphQL

Apresentação do Meetup de Go (Jun, 2020)

  • 1,349