Source:
ocean/docs/api/graphql-reference.md| ✏️ Edit on GitHub
GraphQL API Reference
Endpoint: https://ocean.goldfish.io/api/graphql (Production)
Local: http://127.0.0.1:54321/functions/v1/graphql-v2
Authentication: JWT Bearer token required for all operations except introspection.
Authentication
Headers
Authorization: Bearer <jwt_token>
Content-Type: application/json
Get JWT Token
import { supabaseClient } from '@/lib/supabase'
const {
data: { session },
} = await supabaseClient.auth.getSession()
const token = session?.access_token
Core Queries
System Status
query SystemStatus {
system_status {
healthy
timestamp
environment
cache_enabled
}
}
My Organizations
query MyOrganizations {
my_organizations {
id
name
slug
plan
stripe_subscription_status
billing_email
created_at
updated_at
}
}
Organization Details
query Organization($id: ID!) {
organization(id: $id) {
id
name
slug
plan
owner_id
# Stripe fields
stripe_customer_id
stripe_subscription_id
stripe_subscription_status
stripe_price_id
default_payment_method_id
# Billing
billing_email
tax_id
billing_address
# Subscription info
current_period_start
current_period_end
cancel_at_period_end
# Database info
neon_database_url
neon_project_id
database_ready
created_at
updated_at
}
}
Organization Members
query OrganizationMembers($organization_id: ID!) {
organization_members(organization_id: $organization_id) {
id
user_id
organization_id
role
created_at
# User profile info
user {
id
email
full_name
}
}
}
Billing Queries
Available Products
query AvailableProducts {
available_products {
id
name
description
active
default_price_id
prices {
id
unit_amount
currency
recurring_interval
recurring_interval_count
}
}
}
Billing Details
query BillingDetails {
billing_details {
subscription {
id
status
current_period_start
current_period_end
cancel_at_period_end
trial_end
price {
id
unit_amount
currency
recurring_interval
product {
id
name
description
}
}
}
customer {
id
email
default_source
balance
}
payment_methods {
id
type
card {
brand
last4
exp_month
exp_year
}
}
upcoming_invoice {
id
amount_due
currency
period_start
period_end
}
}
}
Payment Methods
query PaymentMethods {
payment_methods {
id
type
created
card {
brand
last4
exp_month
exp_year
funding
}
billing_details {
name
email
phone
}
}
}
Invoices
query Invoices($limit: Int = 10, $offset: Int = 0) {
invoices(limit: $limit, offset: $offset) {
edges {
id
number
status
amount_paid
amount_due
currency
created
period_start
period_end
hosted_invoice_url
invoice_pdf
lines {
id
description
amount
quantity
period_start
period_end
price {
id
unit_amount
currency
product {
name
description
}
}
}
}
page_info {
has_next_page
has_previous_page
total_count
}
}
}
Usage Metrics
query UsageMetrics($start_date: String, $end_date: String) {
usage_metrics(start_date: $start_date, end_date: $end_date) {
database_storage_mb
api_requests
data_transfer_gb
compute_hours
period_start
period_end
breakdown {
metric_name
current_value
previous_value
percentage_change
}
}
}
Core Mutations
Sign Up (Organization Creation)
mutation SignUp($input: SignUpInput!) {
signUp(input: $input) {
success
organization_id
message
errors
}
}
# Input type
input SignUpInput {
organization_name: String!
user_email: String!
plan: String = "free"
}
Update Organization
mutation UpdateOrganization($id: ID!, $input: UpdateOrganizationInput!) {
updateOrganization(id: $id, input: $input) {
id
name
slug
billing_email
tax_id
billing_address
}
}
# Input type
input UpdateOrganizationInput {
name: String
billing_email: String
tax_id: String
billing_address: JSON
}
Billing Mutations
Create Subscription
mutation CreateSubscription($input: CreateSubscriptionInput!) {
create_subscription(input: $input) {
success
subscription_id
client_secret
status
message
errors
}
}
# Input type
input CreateSubscriptionInput {
price_id: String!
payment_method_id: String
promotion_code: String
}
Update Subscription
mutation UpdateSubscription($input: UpdateSubscriptionInput!) {
update_subscription(input: $input) {
success
subscription_id
status
message
errors
}
}
# Input type
input UpdateSubscriptionInput {
price_id: String
proration_behavior: String = "create_prorations"
}
Cancel Subscription
mutation CancelSubscription($immediately: Boolean = false) {
cancel_subscription(immediately: $immediately) {
success
subscription_id
status
canceled_at
message
errors
}
}
Create Setup Intent (for payment methods)
mutation CreateSetupIntent {
create_setup_intent {
client_secret
status
}
}
Attach Payment Method
mutation AttachPaymentMethod($payment_method_id: String!) {
attach_payment_method(payment_method_id: $payment_method_id) {
id
type
card {
brand
last4
exp_month
exp_year
}
}
}
Customer Portal Session
mutation CreateCustomerPortalSession($return_url: String!) {
create_customer_portal_session(return_url: $return_url) {
url
}
}
Provisioning Mutations
Provision User Resources
mutation ProvisionUserResources($email: String!, $metadata: JSON) {
provision_user_resources(email: $email, metadata: $metadata) {
success
organization_id
neon_project_id
neon_database_url
message
errors
}
}
Retry Provisioning
mutation RetryProvisioning($organization_id: ID!) {
retry_provisioning(organization_id: $organization_id) {
success
organization_id
neon_project_id
neon_database_url
message
errors
}
}
Error Handling
Standard Error Response
interface GraphQLError {
message: string
locations?: Array<{
line: number
column: number
}>
path?: Array<string | number>
extensions?: {
code: string
exception?: {
stacktrace?: string[]
}
}
}
Common Error Codes
UNAUTHENTICATED- No valid JWT token providedFORBIDDEN- User lacks required permissionsNOT_FOUND- Resource doesn't exist or user can't access itVALIDATION_ERROR- Input validation failedSTRIPE_ERROR- Stripe API errorDATABASE_ERROR- Database operation failedPROVISIONING_ERROR- Resource provisioning failed
Error Handling Example
import { request } from 'graphql-request'
try {
const data = await request('/api/graphql', query, variables, {
Authorization: `Bearer ${token}`,
})
} catch (error) {
if (error.response?.errors) {
error.response.errors.forEach((gqlError: GraphQLError) => {
console.error(`GraphQL Error: ${gqlError.message}`)
switch (gqlError.extensions?.code) {
case 'UNAUTHENTICATED':
// Redirect to login
break
case 'STRIPE_ERROR':
// Show billing error message
break
default:
// Show generic error
}
})
}
}
Usage with React Query
Query Hook Example
import { useQuery } from '@tanstack/react-query'
import { graphqlClient } from '@/lib/graphql'
const MY_ORGANIZATIONS_QUERY = `
query MyOrganizations {
my_organizations {
id
name
slug
plan
}
}
`
export const useMyOrganizations = () => {
return useQuery({
queryKey: ['organizations'],
queryFn: async () => {
const data = await graphqlClient.request(MY_ORGANIZATIONS_QUERY)
return data.my_organizations
},
})
}
Mutation Hook Example
import { useMutationWithToast } from '@/hooks/use-mutation-with-toast'
import { graphqlClient } from '@/lib/graphql'
const UPDATE_ORGANIZATION_MUTATION = `
mutation UpdateOrganization($id: ID!, $input: UpdateOrganizationInput!) {
updateOrganization(id: $id, input: $input) {
id
name
billing_email
}
}
`
export const useUpdateOrganization = () => {
return useMutationWithToast({
mutationFn: async ({ id, input }: { id: string; input: any }) => {
const data = await graphqlClient.request(UPDATE_ORGANIZATION_MUTATION, { id, input })
return data.updateOrganization
},
successMessage: 'Organization updated successfully!',
invalidateQueries: [['organization', id], ['organizations']],
})
}
GraphQL Client Setup
Basic Client
// lib/graphql.ts
import { GraphQLClient } from 'graphql-request'
import { supabaseClient } from './supabase'
const endpoint =
process.env.NODE_ENV === 'development'
? 'http://127.0.0.1:54321/functions/v1/graphql-v2'
: 'https://ocean.goldfish.io/api/graphql'
export const graphqlClient = new GraphQLClient(endpoint, {
requestMiddleware: async (request) => {
const {
data: { session },
} = await supabaseClient.auth.getSession()
if (session?.access_token) {
request.headers = {
...request.headers,
Authorization: `Bearer ${session.access_token}`,
}
}
return request
},
})
Introspection
Get Schema
# Using GraphQL CLI
npx get-graphql-schema https://ocean.goldfish.io/api/graphql > schema.graphql
# Using curl
curl -X POST https://ocean.goldfish.io/api/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ __schema { types { name } } }"}'
Code Generation
# Generate TypeScript types
pnpm run codegen:graphql
This generates typed hooks and operations in src/generated/graphql.ts.
Rate Limiting
- Query rate limit: 1000 requests per minute per user
- Mutation rate limit: 100 requests per minute per user
- Billing operations: 10 requests per minute per user
- Provisioning operations: 5 requests per hour per organization
Rate limit headers included in responses:
X-RateLimit-Limit: Request limitX-RateLimit-Remaining: Remaining requestsX-RateLimit-Reset: Reset time (Unix timestamp)
Best Practices
Query Optimization
- Request only fields you need
- Use fragments for repeated field sets
- Batch related queries when possible
- Cache frequently accessed data
Error Handling
- Always handle GraphQL errors
- Show user-friendly error messages
- Log errors for debugging
- Implement retry logic for transient failures
Security
- Never expose JWT tokens in URLs or logs
- Validate all inputs on frontend and backend
- Use HTTPS in production
- Implement proper CORS policies