quickstart
graphql

Quick Start

Get a full GraphQL API running in under 2 minutes.

Prerequisites

  • Docker and Docker Compose installed

Start with PostgreSQL

git clone https://github.com/excalibase/excalibase-graphql.git
cd excalibase-graphql
docker-compose up -d

This starts PostgreSQL, NATS, excalibase-watcher, the GraphQL server on port 10000, and GraphiQL on port 20000.

Open http://localhost:20000 for the GraphiQL playground, or send requests directly to http://localhost:10000/graphql.

Start with MySQL

docker-compose -f docker-compose.mysql.yml up -d

MySQL starts on the default port, GraphQL server on port 10001.

Your First Query

{
  publicFilm(
    where: { rating: { eq: "PG" } }
    orderBy: { rental_rate: ASC }
    limit: 5
  ) {
    title
    rental_rate
    length
    publicActor {
      first_name
      last_name
    }
  }
}

The publicActor field works automatically because film_actor links film and actor via foreign keys — no JOIN needed.

Schema prefix convention: All GraphQL types and fields are prefixed with the schema name. Since the default PostgreSQL schema is public, the prefix is public — e.g., film becomes publicFilm, Film becomes PublicFilm. For MySQL, the prefix is the database name.

What Gets Generated

For every table, Excalibase generates a full set of operations. Given a table film:

type Query {
  # List with filtering, ordering, pagination
  publicFilm(where: PublicFilmFilter, orderBy: PublicFilmOrderBy, limit: Int, offset: Int): [PublicFilm]

  # Cursor-based pagination (Relay-style connection)
  publicFilmConnection(first: Int, after: String): PublicFilmConnection

  # Aggregate functions (count, sum, avg, min, max)
  publicFilmAggregate(where: PublicFilmFilter): PublicFilmAggregate
}

type Mutation {
  createPublicFilm(input: PublicFilmInput!): PublicFilm
  updatePublicFilm(input: PublicFilmInput!): PublicFilm
  deletePublicFilm(input: PublicFilmPkInput!): PublicFilm
  createManyPublicFilm(inputs: [PublicFilmInput!]!): [PublicFilm]
}

type Subscription {
  # Real-time change events (via excalibase-watcher + NATS)
  publicFilmChanges: PublicFilmChangeEvent
}

The filter input (PublicFilmFilter) includes 15 operators per column — eq, neq, gt, lt, in, contains, isNull, and more. See Filtering for the full list.

CRUD Examples

Create

mutation {
  createPublicFilm(input: {
    title: "The Excalibase Movie"
    rental_rate: 2.99
    rental_duration: 7
    replacement_cost: 14.99
    language_id: 1
  }) {
    film_id
    title
  }
}

Update

mutation {
  updatePublicFilm(input: {
    film_id: 1001
    rental_rate: 1.99
  }) {
    film_id
    title
    rental_rate
  }
}

Delete

mutation {
  deletePublicFilm(input: { film_id: 1001 }) {
    film_id
    title
  }
}

Bulk Insert

mutation {
  createManyPublicFilm(inputs: [
    { title: "Film A", rental_rate: 0.99, rental_duration: 3, replacement_cost: 9.99, language_id: 1 }
    { title: "Film B", rental_rate: 4.99, rental_duration: 7, replacement_cost: 19.99, language_id: 1 }
  ]) {
    film_id
    title
  }
}

Configuration

Docker / environment variables

SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/yourdb
SPRING_DATASOURCE_USERNAME=youruser
SPRING_DATASOURCE_PASSWORD=yourpass
APP_SCHEMAS=public

application.yaml

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/yourdb
    username: youruser
    password: yourpass

app:
  schemas: public

Configuration reference

KeyDefaultDescription
app.schemaspublicComma-separated list of schemas to expose, or ALL for auto-discovery
app.database-typepostgresqlpostgresql or mysql
server.port10000HTTP port

All tables in the configured schemas are automatically exposed. There is no include/exclude list — control access at the database level with views or RLS policies.

Try the Playground

Want to explore a live schema without setting up a database?

Open Playground — pre-loaded with the DVD Rental schema