# An introduction to GraphQL

> Learn what GraphQL is and how to write queries with fields, arguments, variables, aliases, fragments, directives, and mutations.

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2018-01-30 | Updated: 2026-07-18 | Topics: [GraphQL](https://flaviocopes.com/tags/graphql/) | Canonical: https://flaviocopes.com/graphql/

GraphQL is a query language for APIs and a specification for executing those queries.

A GraphQL API describes its data with a typed schema. The client asks for the fields it needs, and the response follows the shape of that request.

GraphQL is not a database and it is not tied to a particular programming language or transport protocol. You can put it in front of SQL, a document database, other APIs, or any combination of data sources.

## The schema defines what you can ask for

Here is a small schema written with the GraphQL Schema Definition Language:

```graphql
type Query {
  person(id: ID!): Person
}

type Person {
  id: ID!
  name: String!
  age: Int
  friends(limit: Int): [Person!]!
}
```

The `Query` type is the entry point for read operations. It says that the client can ask for a person by ID.

The `!` after a type means that the value cannot be `null`.

## Write a query

A query contains a selection set: the fields you want the server to return.

```graphql
query GetPerson {
  person(id: "42") {
    name
    age
  }
}
```

The response has a top-level `data` key and mirrors the selected fields:

```json
{
  "data": {
    "person": {
      "name": "Roger",
      "age": 28
    }
  }
}
```

If something goes wrong, the response can also contain a top-level `errors` array. A response may contain both partial `data` and `errors`, so clients should account for that possibility.

Naming operations, as in `GetPerson`, makes requests easier to identify in logs and developer tools.

## Fields and arguments

Fields can have arguments. In the previous query, `id` is an argument of the `person` field.

Arguments can also appear on nested fields:

```graphql
query GetPerson {
  person(id: "42") {
    name
    friends(limit: 3) {
      name
    }
  }
}
```

The available fields and arguments come from the server's schema. You cannot request arbitrary properties.

## Use variables for dynamic values

Do not build queries by interpolating user input into a string. Declare variables in the operation and send their values separately:

```graphql
query GetPerson($personId: ID!) {
  person(id: $personId) {
    name
    age
  }
}
```

The variables are a separate JSON object:

```json
{
  "personId": "42"
}
```

Variables are validated against their declared types before the operation runs.

You can give an optional variable a default value:

```graphql
query GetPerson($personId: ID = "42") {
  person(id: $personId) {
    name
  }
}
```

## Rename fields with aliases

Aliases let you request the same field more than once with different arguments:

```graphql
query ComparePeople {
  first: person(id: "1") {
    name
  }
  second: person(id: "2") {
    name
  }
}
```

The response keys will be `first` and `second`.

## Reuse fields with fragments

A fragment is a reusable selection set:

```graphql
fragment PersonSummary on Person {
  id
  name
  age
}

query ComparePeople {
  first: person(id: "1") {
    ...PersonSummary
  }
  second: person(id: "2") {
    ...PersonSummary
  }
}
```

Fragments are useful when several parts of an interface need the same fields.

## Include fields conditionally

The GraphQL specification includes the `@include` and `@skip` directives. They can conditionally include a field or fragment:

```graphql
query GetPerson($personId: ID!, $withAge: Boolean!) {
  person(id: $personId) {
    name
    age @include(if: $withAge)
  }
}
```

When `withAge` is `false`, the response does not contain the `age` field.

## Queries, mutations, and subscriptions

GraphQL has three operation types:

- `query` reads data
- `mutation` changes data or triggers a side effect
- `subscription` receives updates over time

A mutation still asks for a selection set:

```graphql
mutation RenamePerson($id: ID!, $name: String!) {
  renamePerson(id: $id, name: $name) {
    id
    name
  }
}
```

The schema determines which mutation fields are available and what they return.

## Sending GraphQL over HTTP

GraphQL itself does not require HTTP, but HTTP is the most common transport. A service typically exposes one endpoint such as `/graphql`.

Query and mutation operations can be sent with `POST`. The JSON request body commonly contains `query`, `variables`, and optionally `operationName`:

```js
const response = await fetch('/graphql', {
  method: 'POST',
  headers: {
    'content-type': 'application/json',
    accept: 'application/graphql-response+json',
  },
  body: JSON.stringify({
    query: `
      query GetPerson($personId: ID!) {
        person(id: $personId) {
          name
        }
      }
    `,
    variables: {
      personId: '42',
    },
    operationName: 'GetPerson',
  }),
})

const result = await response.json()
```

Servers may also accept `GET` for query operations. Mutations must use `POST`.

For more details, see the official guides to [GraphQL queries](https://graphql.org/learn/queries/), [schemas and types](https://graphql.org/learn/schema/), and [serving GraphQL over HTTP](https://graphql.org/learn/serving-over-http/).
