---
# System prepended metadata

title: Export types to FTL schema
tags: [implemented]

---

# Export types to FTL schema
**Author**: Lizzy and Wes
**Status**: ==draft== / in-review / accepted / rejected

## Description

Define how types, verbs, etc. are exported to FTL.

## Goals

- Determine how types are exported to the FTL schema
- Allow standalone data types to be exported

## Non-Goals (optional)

- What are we explicitly not doing?
- ...

## Design

Each export strategy below results in the following FTL schema:
```graphql
module example {
  data Request {
    name String
    body example.NestedType
  }

  data Response {
    message String
  }
    
  data NestedType {
  }
    
  data ExportedType {
    field String
  }
  
  verb exportedVerb(example.Request) example.Response
}
```

### Explicit export

Declarations must be explicitly annotated in order to be exported to the FTL schema. If an unexported type is referenced transitively by an exported type (e.g. an exported verb uses an unexported request type), the build will fail, requiring the user to export all dependent types. 

```go
package example

import ...

//ftl:export
type Request struct {
   Name string
   Body NestedType
}

//ftl:export
type Response struct {
   Message string
}

//ftl:export
type NestedType struct {
}

//ftl:export
type ExportedType struct {
    Field string
}

type UnexportedType struct {
}

//ftl:export
func ExportedVerb(ctx context.Context, req Request) (Response, error) {
    return Response{}, nil
}

func UnexportedVerb(ctx context.Context, req Request) (Response, error) {
	return Response{}, nil
}

```

Pros:
- Explicitly distinguishes schema declarations from language code. We can think of the `ftl:export` annotation as a more lightweight version of writing proto code. 
    - We don't support exporting external types to the schema directly, and this distinction provides more clarity around the schema/business logic boundary.

Cons:
- Requires the most user management of the options. Will result in more build failures. Generally the most work.

### Implicit export

If a "verb" is tagged with `ftl:export` that verb and it's dependencies will be automatically exported to FTL. Standalone types can also be annotated with `ftl:export` to be exported to the schema.

```go
package example

import ...

type Request struct {
    Name string
    Body NestedType
}

type Response struct {
    Message string
}

type NestedType struct {
}

//ftl:export
type ExportedType struct {
    Field string
}

type UnexportedType struct {
}

//ftl:export
func ExportedVerb(ctx context.Context, req Request) (Response, error) {
    return Response{}, nil
}

func UnexportedVerb(ctx context.Context, req Request) (Response, error) {
    return Response{}, nil
}
```

Pros:
- Prevents build failures by handling transitive exports. It could be cumbersome to maintain exports explicitly, so this approach ensures that when something is exported, all of its dependencies are as well.

Cons:
- There is ambiguity around what is imported and what is exported. This is a maintainability concern as dependency layers become further nested. 
    - Code does not reliably document what is imported and what is exported - at a glance, some things are annotated with `ftl:export` and others aren't, but even those that aren't may in practice be exported.

### Automatic export

Verbs and types will be exported based on the language conventions. Public will be export, private/internal will not.

> May be useful to add an `ftl:hidden` or `ftl:ignore` annotation to opt out of exporting

```go
package echo

import ...

type Request struct {
    Name string
    Body NestedType
}

type Response struct {
    Message string
}

type NestedType struct {
}

type ExportedType struct {
    Field string
}

type unexportedType struct {
}

func ExportedVerb(ctx context.Context, req Request) (Response, error) {
    return Response{}, nil
}

func unexportedVerb(ctx context.Context, req Request) (Response, error) {
    return Response{}, nil
}

```

Pros:
- Requires the least work from users.
- Is in-keeping with the FTL tenet of relying on runtime conventions when possible, rather than imposing FTL-specific practices.

Cons:
- It's easy to accidentally export things in many runtimes (Go, Python)—FTL will not protect against this.
- The boundary between schema and business logic is obscured; where we don't allow external types in the FTL schema, this may become confusing (i.e. build failures when external types are refenced by exported types).

## Rejected Alternatives

