---
description:
  Learn how to configure sources with no definition using GraphQL Mesh. Use the jsonSchema handler
  to provide a definition of APIs that don't provide an API definition.
---

import { Callout } from '@theguild/components'

# How to: Configure Sources with no definition

GraphQL Mesh provides an extensive range of _Handlers_ (OpenAPI, gRPC, SOAP, GraphQL, and even
Databases!), however, you might try to configure a _Source_ that does not provide an API definition.

We will again use the
["Books" example REST API](https://github.com/charlypoly/graphql-mesh-docs-first-gateway/tree/master/packages/books-service):

- Books API (REST API)
  - `GET /books`
  - `GET /books/:id`
  - `GET /categories`

However, let's pretend that the "Books" API is not providing any OpenAPI definition file:

```mermaid
graph TD;
subgraph BB [" "]
  G[Books REST API without OpenAPI definition];
end
Z[Mesh Gateway GraphQL API]
Z --> G;
```

Once again, GraphQL Mesh gets you covered with the `@graphql-mesh/json-schema` handler that will
help provide a definition of the API.

## An overview of the `jsonSchema` handler

A `jsonSchema` handler configuration must provide a set of `operations` that define the Query and
Mutation to expose.

A standard `jsonSchema` handler configuration will look at the following:

```yaml filename=".meshrc.yaml"
sources:
  - name: MyApi
    handler:
      jsonSchema:
        endpoint: https://some-service-url/endpoint-path/
        operations:
          - type: Query
            field: users
            path: /users
            method: GET
            responseSample: ./samples/users.json
```

`operations` defines a set of GraphQL queries or mutations mapped to some API endpoints (`path`,
`method`)

Above, the `users` Query targets the `GET /users` endpoint.

Finally, we provide `responseSample` that points to a sample file of the Query response.

By using the `responseSample` file, GraphQL Mesh will be able to generate a GraphQL definition of
the `users` Query.

Let's put it in practice with our "Books" REST API `GET /book/:id` endpoint.

## Configuring our "Books" REST API

You will find all the source code of the below example in the dedicated repository:
[`graphql-mesh-docs-first-gateway`](https://github.com/charlypoly/graphql-mesh-docs-first-gateway/tree/master/packages/single-source-no-source-definition).

After installing the `@graphql-mesh/json-schema` package, a good starting point for our "Books" REST
API `jsonSchema` handler configuration would be the following
[`.meshrc.yaml`](https://github.com/charlypoly/graphql-mesh-docs-first-gateway/tree/master/packages/single-source-no-source-definition/.meshrc.yaml):

```yaml filename=".meshrc.yaml"
sources:
  - name: Books
    handler:
      jsonSchema:
        endpoint: http://localhost:3002
```

We need to provide some `operations` for the `GET /book/:id` along with some sample data.

We can get some sample data by running the following commands
([at the root of the project](https://github.com/charlypoly/graphql-mesh-docs-first-gateway)):

1. Build and start the Books REST API

```sh
yarn workspace books-service run build # compiles TypeScript
yarn workspace books-service run start # runs the Nest server
```

2. In a separate terminal, run the following `curl` command:

```sh
curl --location --request GET 'http://localhost:3002/books/1' > packages/single-source-no-source-definition/samples/book-1.json
```

Which will create the following
[`packages/single-source-no-source-definition/samples/book-1.json`](https://github.com/charlypoly/graphql-mesh-docs-first-gateway/tree/master/packages/single-source-no-source-definition/samples/book-1.json)
file:

```json filename="book-1.json"
{ "id": "1", "title": "Dune", "authorId": "0", "categoryId": "0" }
```

We can now provide use this sample file to configure our `Query.book` operation as follows
[`.meshrc.yaml`](https://github.com/charlypoly/graphql-mesh-docs-first-gateway/tree/master/packages/single-source-no-source-definition/.meshrc.yaml):

```yaml filename=".meshrc.yaml"
sources:
  - name: Books
    handler:
      jsonSchema:
        endpoint: http://localhost:3002
        operations:
          - type: Query
            field: book
            path: /books/{args.id}
            method: GET
            responseSample: ./samples/book-1.json
```

<Callout>Note: URL argument should be defined using the `{arg.<name>}` convention</Callout>

Let's build and start our Gateway by running:

```sh
yarn run single-source-no-source-definition
```

Once built, you will find the Unified Schema definition at
[`packages/single-source-no-source-definition/.mesh/schema.graphql`](https://github.com/charlypoly/graphql-mesh-docs-first-gateway/tree/master/packages/single-source-no-source-definition/.mesh/schema.graphql):

```graphql filename="schema.graphql"
type Query {
  book(id: ID): query_book
}

type query_book {
  id: String
  title: String
  authorId: String
  categoryId: String
}
```

<Callout>
  Note: The `query_book` type has been generated thanks to our
  `packages/single-source-no-source-definition/samples/book-1.json` sample file.
</Callout>

We can open the browser at and http://0.0.0.0:4000 try the following Query with GraphiQL:

```graphql
{
  book(id: "1") {
    id
    title
    authorId
  }
}
```

We successfully added a Source without definition! 🎉

## Going further

### Rename generated types

Our `Query.book` `jsonSchema` configuration generates a `query_book` type which is poorly named and
using _snake_case_.

The `responseTypeName` allows us to provide a type name that we will be used for the generated type,
as follows
[`.meshrc.yaml`](https://github.com/charlypoly/graphql-mesh-docs-first-gateway/tree/master/packages/single-source-no-source-definition/.meshrc.yaml):

```yaml filename=".meshrc.yaml"
sources:
  - name: Books
    handler:
      jsonSchema:
        endpoint: http://localhost:3002
        operations:
          - type: Query
            field: book
            path: /books/{args.id}
            method: GET
            responseSample: ./samples/book-1.json
            responseTypeName: Book
```

Which produces the following Unified Schema:

```graphql
type Query {
  book(id: ID): Book
}

type Book {
  id: String
  title: String
  authorId: String
  categoryId: String
}
```

The same parameter exists for request: `requestTypeName` (see "_Mutations_").

### Queries/Mutations arguments

We saw that `operations[].path` can take arguments using syntax similar to string interpolation
[`.meshrc.yaml`](https://github.com/charlypoly/graphql-mesh-docs-first-gateway/tree/master/packages/single-source-no-source-definition/.meshrc.yaml):

```yaml filename=".meshrc.yaml"
sources:
  - name: Books
    handler:
      jsonSchema:
        endpoint: http://localhost:3002
        operations:
          - type: Query
            field: book
            path: /books/{args.id}
            method: GET
            responseSample: ./samples/book-1.json
```

GraphQL Mesh will assign the `ID` type to all arguments by default.

Now, let's say that the "Books" API introduces a search endpoint as follows:

`GET /books/search?q=<query>&categoryId=<categoryId>`

We would need the `query` argument to be of type `String` (not `ID`).

To achieve this, we will leverage the `argTypeMap` parameter as follow:

```yaml filename=".meshrc.yaml"
sources:
  - name: Books
    handler:
      jsonSchema:
        endpoint: http://localhost:3002
        operations:
          - type: Query
            field: search
            path: /books/search?q={args.query}&categoryId={args.categoryId}
            method: GET
            responseSample: ./samples/book-search.json
            argTypeMap:
              query:
                type: string
                nullable: false
```

Now, GraphQL Mesh knows that the type definition of our search query is:

`Query.search(query: String!, categoryId: ID)`.

### Mutations

Since Mutations are most likely to rely on `POST` requests, we need to provide both a
`requestSample` and a `responseSample` parameters.

For example, if our "Books" API exposed a `POST /books`, we could define the following
`Mutation.addBook(…)` mutation:

```yaml filename=".meshrc.yaml"
sources:
  - name: Books
    handler:
      jsonSchema:
        endpoint: http://localhost:3002
        operations:
          - type: Mutation
            field: addBook
            path: /books
            method: POST
            requestSample: ./samples/add-book-request.json
            responseSample: ./samples/add-book-response.json
```

### `Query`/`Mutation` with multiple response shapes

Providing `responseSample/requestSample` is an efficient way to configure a Source without API
definition.

However, the sample files might not always represent all the variants of a given endpoint.

For example, our previous `POST /books` endpoint could return a totally different response shape
depending on the scenario:

**Successfully book creation response**

```json
{
  "id": "1",
  "title": "Dune",
  "authorId": "0",
  "categoryId": "0"
}
```

**Unauthorized response**

```json
{ "error": "Unauthorized" }
```

**Duplicate book response**

```json
{
  "error": "Duplicate",
  "duplicateOf": {
    "id": "1",
    "title": "Dune",
    "authorId": "0",
    "categoryId": "0"
  }
}
```

Relying on `curl` to get a sample of all those scenarios is impossible.

In that case, we will to provide a `responseSchema` parameter that will point to a JSON Schema file:

```yaml filename=".meshrc.yaml"
sources:
  - name: Books
    handler:
      jsonSchema:
        endpoint: http://localhost:3002
        operations:
          - type: Mutation
            field: addBook
            path: /books
            method: POST
            requestSample: ./samples/add-book-request.json
            responseSchema: ./schemas/add-book-response.json#/definitions/AddBookOutput
```

This guide doesn't provide any materials to learn JSON Schema (this would require multiple guides).

However, you can get started on the official
[JSON Schema tutorial](https://json-schema.org/learn/getting-started-step-by-step.html).

Our `./schema/add-book-response.json` would look as follows:

```json filename="add-book-response.json"
{
  "type": "object",
  "properties": {},
  "definitions": {
    "AddBookOutput": {
      "title": "AddBookOutput",
      "oneOf": [
        {
          "$ref": "#/definitions/Book"
        },
        {
          "$ref": "#/definitions/AddBookOutputUnauthorized"
        },
        {
          "$ref": "#/definitions/AddBookOutputDuplicate"
        }
      ]
    },
    "Book": {
      "title": "Book",
      "type": "object",
      "required": ["id"],
      "properties": {
        "id": {
          "type": "string"
        },
        "title": {
          "type": "string"
        },
        "authorId": {
          "type": "string"
        },
        "categoryId": {
          "type": "string"
        }
      }
    },
    "AddBookOutputUnauthorized": {
      "title": "AddBookOutputUnauthorized",
      "type": "object",
      "required": ["error"],
      "properties": {
        "error": {
          "type": "string"
        }
      }
    },
    "AddBookOutputDuplicate": {
      "title": "AddBookOutputDuplicate",
      "type": "object",
      "required": ["error", "duplicateOf"],
      "properties": {
        "error": {
          "type": "string"
        },
        "duplicateOf": {
          "$ref": "#/definitions/Book"
        }
      }
    }
  }
}
```

Building our Mesh Gateway will generate the following Unified Schema:

```graphql
directive @oneOf on INPUT_OBJECT | FIELD_DEFINITION

type Mutation {
  addBook(id: ID): AddBookOutput
}

union AddBookOutput = Book | AddBookOutputUnauthorized | AddBookOutputDuplicate

type Book {
  id: String!
  title: String
  authorId: String
  categoryId: String
}

type AddBookOutputUnauthorized {
  error: String!
}

type AddBookOutputDuplicate {
  error: String!
  duplicateOf: Book!
}
```

Note that you can also leverage the `responseByStatusCode` parameter to provide a schema per HTTP
Status Code, as follows:

```yaml filename=".meshrc.yaml"
operations:
  # …
  responseByStatusCode:
    404:
      responseSchema: ...
    200:
      responseSchema: ...
```
