TypeScript e GraphQL: Criando APIs Fortemente Tipadas

18/09/2024

Por Que Usar TypeScript com GraphQL?

GraphQL é uma linguagem de consulta para APIs que permite solicitar dados de forma precisa e eficiente. Quando combinamos GraphQL com TypeScript, conseguimos criar APIs fortemente tipadas que garantem a integridade dos dados tanto no lado do servidor quanto no cliente. O TypeScript fornece tipagem estática, enquanto o GraphQL assegura que as consultas e mutações sigam a estrutura definida, resultando em uma API mais segura, previsível e fácil de manter.

Configurando TypeScript com GraphQL

Para começar, precisamos configurar um ambiente com Node.js, TypeScript e GraphQL. Aqui está como configurar o projeto.

1. Instalando Dependências

No terminal, execute o seguinte comando para instalar as dependências necessárias:

npm install express express-graphql graphql
npm install --save-dev typescript ts-node @types/express @types/graphql

Os pacotes express e express-graphql são usados para configurar o servidor, graphql fornece o núcleo do GraphQL, e as definições de tipos são instaladas para garantir que o TypeScript entenda os tipos do GraphQL e do Express.

2. Configurando o tsconfig.json

Adicione o arquivo tsconfig.json à raiz do seu projeto com a seguinte configuração básica:

{
  "compilerOptions": {
    "target": "ES6",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}

Esse arquivo configura o TypeScript para compilar o código para ES6 e usar módulos CommonJS, que são padrão no Node.js.

Criando o Esquema GraphQL com TypeScript

Agora que temos as dependências e o ambiente configurados, vamos criar uma API GraphQL simples com TypeScript. Para isso, começaremos criando um esquema básico de GraphQL.

1. Definindo o Esquema

Em src/schema.ts, crie o seguinte código para definir um esquema simples que consulta um Usuario com nome e idade:

import { GraphQLObjectType, GraphQLSchema, GraphQLString, GraphQLInt } from 'graphql';

// Definindo o tipo Usuario
const UsuarioType = new GraphQLObjectType({
  name: 'Usuario',
  fields: {
    nome: { type: GraphQLString },
    idade: { type: GraphQLInt }
  }
});

// Definindo a query de entrada
const RootQuery = new GraphQLObjectType({
  name: 'RootQueryType',
  fields: {
    usuario: {
      type: UsuarioType,
      resolve() {
        return { nome: 'Mauro Souza', idade: 30 };
      }
    }
  }
});

// Criando o esquema
export const schema = new GraphQLSchema({
  query: RootQuery
});

Neste exemplo, definimos um tipo Usuario com os campos nome (string) e idade (inteiro), e uma query de entrada que retorna um objeto de exemplo com esses dados.

2. Configurando o Servidor Express com GraphQL

No arquivo src/index.ts, crie o servidor Express que utiliza GraphQL:

import express from 'express';
import { graphqlHTTP } from 'express-graphql';
import { schema } from './schema';

const app = express();

app.use('/graphql', graphqlHTTP({
  schema,
  graphiql: true
}));

const porta = 4000;
app.listen(porta, () => {
  console.log(`Servidor GraphQL rodando na porta ${porta}`);
});

Aqui, estamos usando o middleware express-graphql para expor o endpoint /graphql e ativar a interface de desenvolvimento GraphiQL, que facilita a execução de consultas e a visualização do esquema.

Executando o Servidor GraphQL

Para iniciar o servidor GraphQL, execute o seguinte comando no terminal:

npx ts-node src/index.ts

O servidor será iniciado na porta 4000. Agora, acesse http://localhost:4000/graphql no navegador para visualizar o GraphiQL e fazer consultas.

1. Exemplo de Consulta

No GraphiQL, faça a seguinte consulta para obter o nome e a idade do usuário:

{
  usuario {
    nome
    idade
  }
}

A resposta será semelhante a:

{
  "data": {
    "usuario": {
      "nome": "Mauro Souza",
      "idade": 30
    }
  }
}

Usando Tipos Personalizados com TypeScript

Para garantir a integridade dos dados no servidor, podemos criar tipos personalizados em TypeScript para tipar o corpo das respostas e a lógica de negócios.

1. Definindo Tipos para o Objeto Usuario

Crie uma interface para o tipo Usuario no arquivo src/types.ts:

export interface Usuario {
  nome: string;
  idade: number;
}

2. Usando a Interface no Resolvedor

Agora, use a interface Usuario no arquivo src/schema.ts para garantir que os dados do usuário estejam corretamente tipados:

import { Usuario } from './types';

const RootQuery = new GraphQLObjectType({
  name: 'RootQueryType',
  fields: {
    usuario: {
      type: UsuarioType,
      resolve(): Usuario {
        return { nome: 'Mauro Souza', idade: 30 };
      }
    }
  }
});

Com isso, o TypeScript garantirá que os dados retornados pela função resolve sigam a estrutura definida pela interface Usuario.

Adicionando Mutações ao Esquema

As mutações permitem modificar dados no servidor. Vamos adicionar uma mutação para criar um novo usuário e retornar seus dados.

1. Exemplo de Mutação

No arquivo src/schema.ts, adicione o seguinte código para definir uma mutação:

const Mutation = new GraphQLObjectType({
  name: 'Mutation',
  fields: {
    criarUsuario: {
      type: UsuarioType,
      args: {
        nome: { type: GraphQLString },
        idade: { type: GraphQLInt }
      },
      resolve(parent, args): Usuario {
        return { nome: args.nome, idade: args.idade };
      }
    }
  }
});

export const schema = new GraphQLSchema({
  query: RootQuery,
  mutation: Mutation
});

A mutação criarUsuario aceita os argumentos nome e idade e retorna o novo usuário criado. O TypeScript garante que os dados de entrada e saída estejam corretos, seguindo a tipagem definida.

2. Fazendo uma Mutação

No GraphiQL, faça a seguinte mutação para criar um novo usuário:

mutation {
  criarUsuario(nome: "João", idade: 25) {
    nome
    idade
  }
}

A resposta será semelhante a:

{
  "data": {
    "criarUsuario": {
      "nome": "João",
      "idade": 25
    }
  }
}

Boas Práticas ao Usar TypeScript com GraphQL

  • Use Interfaces para Tipagem Forte: Sempre defina interfaces para os dados que estão sendo consultados ou modificados, garantindo que o TypeScript valide a estrutura.
  • Combine Resolvedores com Tipos: Garanta que as funções resolve estejam sempre retornando os tipos corretos usando as interfaces TypeScript.
  • Valide Argumentos: Ao definir mutações, valide os argumentos com o GraphQL e adicione validações adicionais no TypeScript para garantir a integridade dos dados recebidos.
  • Organize o Código: Mantenha o esquema, tipos e resolvedores bem organizados em arquivos separados para melhorar a manutenibilidade.

Conclusão

Combinar TypeScript com GraphQL permite criar APIs fortemente tipadas que garantem a integridade dos dados e a segurança durante o desenvolvimento. O uso de tipos no lado do servidor, juntamente com o esquema GraphQL, resulta em APIs mais previsíveis e seguras. Seguindo boas práticas e utilizando as capacidades do TypeScript, você pode construir APIs escaláveis e fáceis de manter, aproveitando ao máximo as vantagens da tipagem estática.