Skip to content

Guide

The GraphQL emitter (@typespec/graphql) generates GraphQL Schema Definition Language (SDL) from TypeSpec sources. The generated schemas can be used with any GraphQL server implementation.

The GraphQL emitter transforms TypeSpec models, operations, and enums into their GraphQL equivalents. To generate a valid GraphQL schema, your TypeSpec must follow certain conventions.

A GraphQL schema is defined by applying the @schema decorator to a TypeSpec namespace. All types and operations within the namespace will be emitted to a single GraphQL schema file.

import "@typespec/graphql";
using GraphQL;
@schema
namespace MyService {
// Types and operations go here
}

You can optionally specify a name for the schema, which will be used in the output filename:

@schema(#{ name: "petstore" })
namespace PetStore {
// ...
}

This will generate a file named petstore.graphql.

TypeSpec models are transformed into GraphQL object types. For example:

model Pet {
id: string;
name: string;
age: int32;
}

Becomes:

type Pet {
id: String!
name: String!
age: Int!
}

Note that all fields are non-nullable (!) by default in GraphQL. See Nullability for how to make fields nullable.

GraphQL supports three operation types: queries, mutations, and subscriptions. Use the corresponding decorators to specify the operation kind:

@query op getPet(id: string): Pet;
@query op listPets(): Pet[];
@mutation op createPet(name: string, age: int32): Pet;
@mutation op deletePet(id: string): boolean;
@subscription op onPetCreated(): Pet;

This generates:

type Query {
getPet(id: String!): Pet!
listPets: [Pet!]!
}
type Mutation {
createPet(name: String!, age: Int!): Pet!
deletePet(id: String!): Boolean!
}
type Subscription {
onPetCreated: Pet!
}

When a model is used as an operation parameter (mutation input), the emitter automatically generates a corresponding GraphQL input type with an Input suffix:

model Pet {
name: string;
age: int32;
}
@mutation op createPet(input: Pet): Pet;

Generates:

input PetInput {
name: String!
age: Int!
}
type Mutation {
createPet(input: PetInput!): Pet!
}

TypeSpec enums map to GraphQL enums with member names converted to CONSTANT_CASE:

enum PetStatus {
Available,
Pending,
Sold,
}

Becomes:

enum PetStatus {
AVAILABLE
PENDING
SOLD
}

By default, all GraphQL types are non-nullable. To make a field or return type nullable, use TypeSpec’s union with null:

model Pet {
id: string;
nickname: string | null; // Nullable field
}
@query op findPet(id: string): Pet | null; // Nullable return

Generates:

type Pet {
id: String!
nickname: String # No ! means nullable
}
type Query {
findPet(id: String!): Pet # Nullable return
}

For nullable array elements, the emitter handles Array<T | null> patterns:

model SearchResult {
pets: (Pet | null)[]; # Array with nullable elements
}

Generates:

type SearchResult {
pets: [Pet]! # Non-null array, nullable elements
}

GraphQL interfaces allow you to define a common set of fields that multiple types can implement. Use the @graphqlInterface decorator:

@graphqlInterface(#{ interfaceOnly: true })
model Node {
id: string;
}
@compose(Node)
model Pet {
...Node;
name: string;
}

Generates:

interface Node {
id: String!
}
type Pet implements Node {
id: String!
name: String!
}

The interfaceOnly option prevents the model from also being emitted as an object type (useful for abstract interfaces like Node).

TypeSpec TypeGraphQL Type
stringString
booleanBoolean
int32Int
float32, float64Float
GraphQL.IDID
T[][T!]!
T | nullT (nullable)

To use the GraphQL emitter, add it to your tspconfig.yaml:

emit:
- "@typespec/graphql"

The emitter will generate .graphql files in your output directory.