The typed client provides a lightweight alternative to Apollo Client, ideal for server-side environments, serverless functions, and scripts.
When to Use the Typed Client
Use Typed Client
- Server-side rendering
- Serverless functions
- CLI tools and scripts
- Simple query/mutation operations
- When you don’t need caching
Use Apollo Client
- Browser applications
- React applications
- When you need caching
- When you need subscriptions
- Complex state management
Basic Usage
import { createTypedClient } from '@well-played.gg/typescript-sdk';
const client = createTypedClient({
organizationId: 'your-org-id',
token: 'your-auth-token'
});
// Make a query
const result = await client.query({
players: {
__args: {
ids: ['player-1'],
page: { first: 10 }
},
nodes: {
id: true,
username: true,
customFields: {
property: true,
value: true
}
}
}
});
console.log(result.players.nodes);
Type Signature
From typed-client.ts:6-48:
export const createTypedClient = (
props: {
apiBaseUrl?: string;
organizationId: string;
token?: string;
application?: {
clientId: string;
clientSecret: string;
};
} & Omit<ClientOptions, "url" | "headers" | "batch" | "keepalive" | "method">,
) => {
const apiBaseUrl = props.apiBaseUrl ?? "well-played.gg";
const url = `https://api.warrior.${apiBaseUrl}/graphql`;
const oauthUrl = `https://oauth.warrior.${apiBaseUrl}`;
return createClient({
url,
batch: {
batchInterval: 100,
maxBatchSize: 10,
},
headers: async () => {
const token =
props.token ??
(props.application
? await getToken(
props.application.clientId,
props.application.clientSecret,
oauthUrl,
)
: undefined);
return omitBy(
{
"organization-id": props.organizationId,
authorization: token ? `Bearer ${token}` : undefined,
},
isNil,
) as Record<string, string>;
},
keepalive: true,
method: "POST",
...omit(props, "apiBaseUrl", "application", "token", "organizationId"),
});
};
export type TypedClient = ReturnType<typeof createTypedClient>;
Configuration Options
User Authentication
Authenticate as a user with a JWT token:
const client = createTypedClient({
organizationId: 'your-org-id',
token: 'user-jwt-token'
});
Application Authentication
Authenticate as an application using client credentials:
const client = createTypedClient({
organizationId: 'your-org-id',
application: {
clientId: process.env.WP_CLIENT_ID!,
clientSecret: process.env.WP_CLIENT_SECRET!
}
});
Application authentication automatically handles OAuth token refresh and caching.
Environment Configuration
const client = createTypedClient({
organizationId: 'your-org-id',
apiBaseUrl: 'stg.well-played.gg', // default: 'well-played.gg'
application: { /* ... */ }
});
Request Batching
The typed client automatically batches multiple queries executed within 100ms:
const client = createTypedClient({
organizationId: 'your-org-id',
token: 'your-token'
});
// These queries will be batched into a single HTTP request
const [players, tournaments] = await Promise.all([
client.query({
players: {
__args: { ids: ['p1'], page: { first: 10 } },
nodes: { id: true, username: true }
}
}),
client.query({
tournaments: {
__args: { page: { first: 10 } },
nodes: { id: true, name: true }
}
})
]);
Batching configuration from typed-client.ts:22-25:
batch: {
batchInterval: 100, // Wait 100ms before sending batch
maxBatchSize: 10, // Max 10 queries per batch
}
Making Queries
Basic Query
const result = await client.query({
tournaments: {
__args: {
page: { first: 20 },
status: 'PUBLISHED'
},
nodes: {
id: true,
name: true,
startDate: true,
status: true
},
pageInfo: {
hasNextPage: true,
endCursor: true
}
}
});
console.log(result.tournaments.nodes);
Nested Queries
const result = await client.query({
tournament: {
__args: { id: 'tournament-1' },
id: true,
name: true,
steps: {
id: true,
name: true,
type: true,
groups: {
id: true,
name: true,
teams: {
id: true,
name: true
}
}
}
}
});
Query with Variables
const getTournamentTeams = async (tournamentId: string) => {
return client.query({
tournamentTeams: {
__args: {
tournamentId,
page: { first: 100 },
memberStatus: 'ACCEPTED',
status: 'CONFIRMED'
},
nodes: {
id: true,
name: true,
members: {
playerProfileId: true,
status: true
}
},
pageInfo: {
hasNextPage: true,
endCursor: true
}
}
});
};
Making Mutations
const result = await client.mutation({
createTournament: {
__args: {
input: {
name: 'My Tournament',
startDate: '2024-01-01T00:00:00Z',
status: 'DRAFT'
}
},
id: true,
name: true,
status: true
}
});
console.log('Created tournament:', result.createTournament.id);
async function getAllPlayers(playerIds: string[]) {
const result: any[] = [];
let hasNextPage = true;
let cursor: string | undefined;
while (hasNextPage) {
const response = await client.query({
players: {
__args: {
ids: playerIds,
page: cursor
? { first: 100, after: cursor }
: { first: 100 }
},
nodes: {
id: true,
username: true,
customFields: {
property: true,
value: true
}
},
pageInfo: {
hasNextPage: true,
endCursor: true
}
}
});
result.push(...response.players.nodes);
hasNextPage = response.players.pageInfo.hasNextPage;
cursor = response.players.pageInfo.endCursor;
}
return result;
}
Server-Side Example (Next.js)
// app/api/tournaments/route.ts
import { createTypedClient } from '@well-played.gg/typescript-sdk';
import { NextResponse } from 'next/server';
export async function GET() {
const client = createTypedClient({
organizationId: process.env.WP_ORG_ID!,
application: {
clientId: process.env.WP_CLIENT_ID!,
clientSecret: process.env.WP_CLIENT_SECRET!
}
});
const result = await client.query({
tournaments: {
__args: {
page: { first: 20 },
status: 'PUBLISHED'
},
nodes: {
id: true,
name: true,
startDate: true
}
}
});
return NextResponse.json(result.tournaments.nodes);
}
Error Handling
import { GenqlError } from '@well-played.gg/typescript-sdk';
try {
const result = await client.query({
tournament: {
__args: { id: 'invalid-id' },
id: true,
name: true
}
});
} catch (error) {
if (error instanceof GenqlError) {
console.error('GraphQL Error:', error.message);
console.error('Errors:', error.errors);
} else {
console.error('Network Error:', error);
}
}
TypeScript Type Safety
The typed client provides full TypeScript autocomplete:
const result = await client.query({
tournaments: {
__args: {
page: { first: 10 }
},
nodes: {
id: true,
name: true,
// TypeScript will autocomplete available fields
// and validate field selections
}
}
});
// Result is fully typed:
result.tournaments.nodes // Array<{ id: string; name: string }>
Comparison with Apollo Client
Typed Client
Apollo Client
const client = createTypedClient({
organizationId: 'org-id',
token: 'token'
});
const result = await client.query({
players: {
__args: {
ids: ['p1'],
page: { first: 10 }
},
nodes: {
id: true,
username: true
}
}
});
import { graphql } from '@well-played.gg/typescript-sdk';
import { useQuery } from '@apollo/client';
const GET_PLAYERS = graphql(`
query getPlayers($ids: [ID!]!, $page: PageInfo!) {
players(ids: $ids, page: $page) {
nodes {
id
username
}
}
}
`);
const { data } = useQuery(GET_PLAYERS, {
variables: {
ids: ['p1'],
page: { first: 10 }
}
});
The typed client does not support GraphQL subscriptions. Use Apollo Client with WebSocket configuration for real-time updates.