☕ 🌐 API Design with TuskLang Java
🌐 API Design with TuskLang Java
"We don't bow to any king" - API Design Java Edition
TuskLang Java enables building well-designed APIs with RESTful principles, GraphQL support, and comprehensive API versioning that scales with your application needs.
🎯 API Design Architecture Overview
API Configuration
// api-design-app.tsk
[api_design]
name: "API Design TuskLang App"
version: "2.0.0"
paradigm: "api_first"
documentation: "openapi"[rest_api]
base_path: "/api"
versioning: "url_path"
versions: ["v1", "v2", "v3"]
default_version: "v2"
endpoints: {
users: {
path: "/users"
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
pagination: {
enabled: true
default_size: 20
max_size: 100
page_parameter: "page"
size_parameter: "size"
}
filtering: {
enabled: true
fields: ["email", "status", "created_at"]
operators: ["eq", "ne", "gt", "lt", "gte", "lte", "like", "in"]
}
sorting: {
enabled: true
fields: ["id", "email", "first_name", "last_name", "created_at"]
default_sort: "created_at:desc"
}
caching: {
enabled: true
ttl: "5m"
etag: true
cache_control: "public, max-age=300"
}
rate_limiting: {
enabled: true
requests_per_minute: 100
burst_size: 10
user_based: true
}
security: {
authentication: "jwt"
authorization: "rbac"
scopes: ["read:users", "write:users", "delete:users"]
}
}
orders: {
path: "/orders"
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
pagination: {
enabled: true
default_size: 20
max_size: 100
page_parameter: "page"
size_parameter: "size"
}
filtering: {
enabled: true
fields: ["user_id", "status", "total_amount", "created_at"]
operators: ["eq", "ne", "gt", "lt", "gte", "lte", "like", "in"]
}
sorting: {
enabled: true
fields: ["id", "user_id", "status", "total_amount", "created_at"]
default_sort: "created_at:desc"
}
caching: {
enabled: true
ttl: "2m"
etag: true
cache_control: "public, max-age=120"
}
rate_limiting: {
enabled: true
requests_per_minute: 200
burst_size: 20
user_based: true
}
security: {
authentication: "jwt"
authorization: "rbac"
scopes: ["read:orders", "write:orders", "delete:orders"]
}
}
products: {
path: "/products"
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
pagination: {
enabled: true
default_size: 50
max_size: 200
page_parameter: "page"
size_parameter: "size"
}
filtering: {
enabled: true
fields: ["category", "price", "stock", "status", "created_at"]
operators: ["eq", "ne", "gt", "lt", "gte", "lte", "like", "in", "between"]
}
sorting: {
enabled: true
fields: ["id", "name", "price", "stock", "created_at"]
default_sort: "name:asc"
}
caching: {
enabled: true
ttl: "10m"
etag: true
cache_control: "public, max-age=600"
}
rate_limiting: {
enabled: true
requests_per_minute: 500
burst_size: 50
user_based: false
}
security: {
authentication: "jwt"
authorization: "rbac"
scopes: ["read:products", "write:products", "delete:products"]
}
}
}
[graphql_api]
enabled: true
path: "/graphql"
playground: {
enabled: true
path: "/graphql-playground"
settings: {
theme: "dark"
show_docs: true
show_schema: true
}
}
introspection: {
enabled: true
production: false
}
subscriptions: {
enabled: true
transport: "websocket"
path: "/graphql-subscriptions"
}
rate_limiting: {
enabled: true
requests_per_minute: 1000
complexity_limit: 1000
depth_limit: 10
}
caching: {
enabled: true
ttl: "5m"
strategy: "field_level"
}
[api_documentation]
openapi: {
version: "3.0.3"
title: "TuskLang API"
description: "Comprehensive API for TuskLang applications"
version: "2.0.0"
contact: {
name: "API Support"
email: "api@tuskt.sk"
url: "https://tuskt.sk/support"
}
license: {
name: "MIT"
url: "https://opensource.org/licenses/MIT"
}
servers: [
{
url: "https://api.tuskt.sk"
description: "Production server"
},
{
url: "https://api-staging.tuskt.sk"
description: "Staging server"
},
{
url: "http://localhost:8080"
description: "Local development server"
}
]
paths: {
generate_from_controllers: true
include_examples: true
include_schemas: true
}
security: {
bearer_auth: {
type: "http"
scheme: "bearer"
bearer_format: "JWT"
}
oauth2: {
type: "oauth2"
flows: {
authorization_code: {
authorization_url: "https://auth.tuskt.sk/oauth/authorize"
token_url: "https://auth.tuskt.sk/oauth/token"
scopes: {
"read:users": "Read user information"
"write:users": "Create and update users"
"delete:users": "Delete users"
"read:orders": "Read order information"
"write:orders": "Create and update orders"
"delete:orders": "Delete orders"
}
}
}
}
}
}
[api_versioning]
strategy: "url_path"
versions: {
v1: {
status: "deprecated"
sunset_date: "2024-12-31"
migration_guide: "https://tuskt.sk/documents/api/migration/v1-to-v2"
}
v2: {
status: "current"
release_date: "2023-01-01"
features: ["enhanced_filtering", "improved_pagination", "graphql_support"]
}
v3: {
status: "beta"
release_date: "2024-01-01"
features: ["real_time_subscriptions", "advanced_analytics", "ai_powered_search"]
}
}
backward_compatibility: {
enabled: true
deprecated_features: {
warning_header: true
deprecation_notice: true
migration_guide: true
}
}
[api_monitoring]
metrics: {
enabled: true
endpoint: "/actuator/metrics"
custom_metrics: [
"request_duration",
"response_size",
"error_rate",
"cache_hit_rate",
"rate_limit_exceeded"
]
}
tracing: {
enabled: true
sampling_rate: 0.1
correlation_id_header: "X-Correlation-ID"
request_id_header: "X-Request-ID"
}
logging: {
enabled: true
level: "INFO"
format: "json"
include_headers: ["Authorization", "User-Agent", "X-Request-ID"]
exclude_headers: ["Cookie", "Set-Cookie"]
mask_sensitive_data: true
}
[api_security]
authentication: {
jwt: {
enabled: true
secret: @env.secure("JWT_SECRET")
algorithm: "HS256"
expiration: "24h"
refresh_expiration: "7d"
issuer: @env("JWT_ISSUER", "tusklang-api")
audience: @env("JWT_AUDIENCE", "tusklang-users")
}
oauth2: {
enabled: true
providers: {
google: {
client_id: @env.secure("GOOGLE_CLIENT_ID")
client_secret: @env.secure("GOOGLE_CLIENT_SECRET")
redirect_uri: @env("GOOGLE_REDIRECT_URI")
scopes: ["openid", "email", "profile"]
}
github: {
client_id: @env.secure("GITHUB_CLIENT_ID")
client_secret: @env.secure("GITHUB_CLIENT_SECRET")
redirect_uri: @env("GITHUB_REDIRECT_URI")
scopes: ["read:user", "user:email"]
}
}
}
}
authorization: {
rbac: {
enabled: true
roles: [
"admin",
"user",
"moderator",
"readonly"
]
permissions: {
"admin": ["*"]
"user": ["read:own", "write:own", "delete:own"]
"moderator": ["read:all", "write:all", "delete:limited"]
"readonly": ["read:all"]
}
}
}
cors: {
enabled: true
allowed_origins: [
"https://app.tuskt.sk",
"https://admin.tuskt.sk",
"http://localhost:3000"
]
allowed_methods: ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"]
allowed_headers: ["Content-Type", "Authorization", "X-Request-ID"]
allow_credentials: true
max_age: 3600
}
rate_limiting: {
enabled: true
strategy: "token_bucket"
default_limit: {
requests_per_minute: 100
burst_size: 10
}
user_limits: {
authenticated: {
requests_per_minute: 1000
burst_size: 100
}
premium: {
requests_per_minute: 5000
burst_size: 500
}
}
storage: "redis"
key_resolver: "user_id"
}
🌐 RESTful API Implementation
API Configuration
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.tusklang.java.annotations.TuskConfig;
import java.util.List;@Configuration
@TuskConfig
public class APIDesignConfiguration implements WebMvcConfigurer {
private final APIDesignConfig apiDesignConfig;
public APIDesignConfiguration(APIDesignConfig apiDesignConfig) {
this.apiDesignConfig = apiDesignConfig;
}
@Bean
public ApiVersioningInterceptor apiVersioningInterceptor() {
return new ApiVersioningInterceptor(apiDesignConfig.getApiVersioning());
}
@Bean
public RateLimitingInterceptor rateLimitingInterceptor() {
return new RateLimitingInterceptor(apiDesignConfig.getApiSecurity().getRateLimiting());
}
@Bean
public CachingInterceptor cachingInterceptor() {
return new CachingInterceptor();
}
@Bean
public CorrelationIdInterceptor correlationIdInterceptor() {
return new CorrelationIdInterceptor(apiDesignConfig.getApiMonitoring().getTracing());
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(correlationIdInterceptor());
registry.addInterceptor(apiVersioningInterceptor());
registry.addInterceptor(rateLimitingInterceptor());
registry.addInterceptor(cachingInterceptor());
}
}
@TuskConfig
public class APIDesignConfig {
private String name;
private String version;
private String paradigm;
private String documentation;
private RestAPIConfig restApi;
private GraphQLAPIConfig graphqlApi;
private APIDocumentationConfig apiDocumentation;
private APIVersioningConfig apiVersioning;
private APIMonitoringConfig apiMonitoring;
private APISecurityConfig apiSecurity;
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public String getParadigm() { return paradigm; }
public void setParadigm(String paradigm) { this.paradigm = paradigm; }
public String getDocumentation() { return documentation; }
public void setDocumentation(String documentation) { this.documentation = documentation; }
public RestAPIConfig getRestApi() { return restApi; }
public void setRestApi(RestAPIConfig restApi) { this.restApi = restApi; }
public GraphQLAPIConfig getGraphqlApi() { return graphqlApi; }
public void setGraphqlApi(GraphQLAPIConfig graphqlApi) { this.graphqlApi = graphqlApi; }
public APIDocumentationConfig getApiDocumentation() { return apiDocumentation; }
public void setApiDocumentation(APIDocumentationConfig apiDocumentation) { this.apiDocumentation = apiDocumentation; }
public APIVersioningConfig getApiVersioning() { return apiVersioning; }
public void setApiVersioning(APIVersioningConfig apiVersioning) { this.apiVersioning = apiVersioning; }
public APIMonitoringConfig getApiMonitoring() { return apiMonitoring; }
public void setApiMonitoring(APIMonitoringConfig apiMonitoring) { this.apiMonitoring = apiMonitoring; }
public APISecurityConfig getApiSecurity() { return apiSecurity; }
public void setApiSecurity(APISecurityConfig apiSecurity) { this.apiSecurity = apiSecurity; }
}
@TuskConfig
public class RestAPIConfig {
private String basePath;
private String versioning;
private List<String> versions;
private String defaultVersion;
private Map<String, EndpointConfig> endpoints;
// Getters and setters
public String getBasePath() { return basePath; }
public void setBasePath(String basePath) { this.basePath = basePath; }
public String getVersioning() { return versioning; }
public void setVersioning(String versioning) { this.versioning = versioning; }
public List<String> getVersions() { return versions; }
public void setVersions(List<String> versions) { this.versions = versions; }
public String getDefaultVersion() { return defaultVersion; }
public void setDefaultVersion(String defaultVersion) { this.defaultVersion = defaultVersion; }
public Map<String, EndpointConfig> getEndpoints() { return endpoints; }
public void setEndpoints(Map<String, EndpointConfig> endpoints) { this.endpoints = endpoints; }
}
RESTful Controller Implementation
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
import java.util.List;@RestController
@RequestMapping("/api/v2/users")
@Validated
@ApiVersion("v2")
public class UserController {
private final UserService userService;
private final UserMapper userMapper;
private final APIMonitoringConfig monitoringConfig;
public UserController(UserService userService,
UserMapper userMapper,
APIMonitoringConfig monitoringConfig) {
this.userService = userService;
this.userMapper = userMapper;
this.monitoringConfig = monitoringConfig;
}
@GetMapping
@ApiOperation("Get all users with pagination and filtering")
@ApiResponses({
@ApiResponse(code = 200, message = "Users retrieved successfully"),
@ApiResponse(code = 400, message = "Invalid request parameters"),
@ApiResponse(code = 401, message = "Unauthorized"),
@ApiResponse(code = 403, message = "Forbidden")
})
public ResponseEntity<PageResponse<UserResponse>> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size,
@RequestParam(required = false) String email,
@RequestParam(required = false) String status,
@RequestParam(required = false) String sort,
@RequestParam(required = false) String order) {
// Build filter criteria
UserFilterCriteria criteria = UserFilterCriteria.builder()
.email(email)
.status(status)
.build();
// Build sort criteria
Sort sortCriteria = Sort.by(Sort.Direction.fromString(order != null ? order : "desc"),
sort != null ? sort : "createdAt");
// Get paginated users
Page<User> users = userService.getUsers(criteria,
PageRequest.of(page, size, sortCriteria));
// Map to response DTOs
List<UserResponse> userResponses = users.getContent().stream()
.map(userMapper::toResponse)
.collect(Collectors.toList());
// Build response
PageResponse<UserResponse> response = PageResponse.<UserResponse>builder()
.data(userResponses)
.page(users.getNumber())
.size(users.getSize())
.totalElements(users.getTotalElements())
.totalPages(users.getTotalPages())
.hasNext(users.hasNext())
.hasPrevious(users.hasPrevious())
.build();
return ResponseEntity.ok()
.cacheControl(CacheControl.maxAge(Duration.ofMinutes(5)))
.eTag(generateETag(response))
.body(response);
}
@GetMapping("/{id}")
@ApiOperation("Get user by ID")
@ApiResponses({
@ApiResponse(code = 200, message = "User retrieved successfully"),
@ApiResponse(code = 404, message = "User not found"),
@ApiResponse(code = 401, message = "Unauthorized"),
@ApiResponse(code = 403, message = "Forbidden")
})
public ResponseEntity<UserResponse> getUserById(@PathVariable String id) {
User user = userService.getUserById(id);
UserResponse response = userMapper.toResponse(user);
return ResponseEntity.ok()
.cacheControl(CacheControl.maxAge(Duration.ofMinutes(10)))
.eTag(generateETag(response))
.body(response);
}
@PostMapping
@ApiOperation("Create a new user")
@ApiResponses({
@ApiResponse(code = 201, message = "User created successfully"),
@ApiResponse(code = 400, message = "Invalid request body"),
@ApiResponse(code = 409, message = "User already exists"),
@ApiResponse(code = 401, message = "Unauthorized"),
@ApiResponse(code = 403, message = "Forbidden")
})
public ResponseEntity<UserResponse> createUser(@Valid @RequestBody CreateUserRequest request) {
User user = userMapper.toEntity(request);
User createdUser = userService.createUser(user);
UserResponse response = userMapper.toResponse(createdUser);
return ResponseEntity.status(HttpStatus.CREATED)
.location(URI.create("/api/v2/users/" + createdUser.getId()))
.body(response);
}
@PutMapping("/{id}")
@ApiOperation("Update user by ID")
@ApiResponses({
@ApiResponse(code = 200, message = "User updated successfully"),
@ApiResponse(code = 400, message = "Invalid request body"),
@ApiResponse(code = 404, message = "User not found"),
@ApiResponse(code = 401, message = "Unauthorized"),
@ApiResponse(code = 403, message = "Forbidden")
})
public ResponseEntity<UserResponse> updateUser(
@PathVariable String id,
@Valid @RequestBody UpdateUserRequest request) {
User user = userMapper.toEntity(request);
user.setId(id);
User updatedUser = userService.updateUser(user);
UserResponse response = userMapper.toResponse(updatedUser);
return ResponseEntity.ok()
.eTag(generateETag(response))
.body(response);
}
@PatchMapping("/{id}")
@ApiOperation("Partially update user by ID")
@ApiResponses({
@ApiResponse(code = 200, message = "User updated successfully"),
@ApiResponse(code = 400, message = "Invalid request body"),
@ApiResponse(code = 404, message = "User not found"),
@ApiResponse(code = 401, message = "Unauthorized"),
@ApiResponse(code = 403, message = "Forbidden")
})
public ResponseEntity<UserResponse> patchUser(
@PathVariable String id,
@RequestBody Map<String, Object> updates) {
User updatedUser = userService.patchUser(id, updates);
UserResponse response = userMapper.toResponse(updatedUser);
return ResponseEntity.ok()
.eTag(generateETag(response))
.body(response);
}
@DeleteMapping("/{id}")
@ApiOperation("Delete user by ID")
@ApiResponses({
@ApiResponse(code = 204, message = "User deleted successfully"),
@ApiResponse(code = 404, message = "User not found"),
@ApiResponse(code = 401, message = "Unauthorized"),
@ApiResponse(code = 403, message = "Forbidden")
})
public ResponseEntity<Void> deleteUser(@PathVariable String id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
@GetMapping("/{id}/orders")
@ApiOperation("Get orders for a specific user")
@ApiResponses({
@ApiResponse(code = 200, message = "Orders retrieved successfully"),
@ApiResponse(code = 404, message = "User not found"),
@ApiResponse(code = 401, message = "Unauthorized"),
@ApiResponse(code = 403, message = "Forbidden")
})
public ResponseEntity<PageResponse<OrderResponse>> getUserOrders(
@PathVariable String id,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
Page<Order> orders = userService.getUserOrders(id, PageRequest.of(page, size));
List<OrderResponse> orderResponses = orders.getContent().stream()
.map(orderMapper::toResponse)
.collect(Collectors.toList());
PageResponse<OrderResponse> response = PageResponse.<OrderResponse>builder()
.data(orderResponses)
.page(orders.getNumber())
.size(orders.getSize())
.totalElements(orders.getTotalElements())
.totalPages(orders.getTotalPages())
.hasNext(orders.hasNext())
.hasPrevious(orders.hasPrevious())
.build();
return ResponseEntity.ok()
.cacheControl(CacheControl.maxAge(Duration.ofMinutes(2)))
.eTag(generateETag(response))
.body(response);
}
private String generateETag(Object object) {
try {
String json = objectMapper.writeValueAsString(object);
return DigestUtils.md5DigestAsHex(json.getBytes());
} catch (Exception e) {
return UUID.randomUUID().toString();
}
}
}
🔍 GraphQL API Implementation
GraphQL Configuration
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;@Configuration
@TuskConfig
public class GraphQLConfiguration {
private final GraphQLAPIConfig graphqlConfig;
public GraphQLConfiguration(GraphQLAPIConfig graphqlConfig) {
this.graphqlConfig = graphqlConfig;
}
@Bean
public GraphQLSchema graphQLSchema() {
SchemaParser schemaParser = new SchemaParser();
SchemaGenerator schemaGenerator = new SchemaGenerator();
// Load schema from file
String schema = loadSchemaFromFile();
TypeDefinitionRegistry typeRegistry = schemaParser.parse(schema);
// Build runtime wiring
RuntimeWiring runtimeWiring = buildRuntimeWiring();
return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
}
@Bean
public GraphQL graphQL(GraphQLSchema schema) {
return GraphQL.newGraphQL(schema)
.instrumentation(new TracingInstrumentation())
.instrumentation(new DataLoaderInstrumentation())
.build();
}
private RuntimeWiring buildRuntimeWiring() {
return RuntimeWiring.newRuntimeWiring()
.type("Query", typeWiring -> typeWiring
.dataFetcher("users", userDataFetcher())
.dataFetcher("user", userByIdDataFetcher())
.dataFetcher("orders", orderDataFetcher())
.dataFetcher("order", orderByIdDataFetcher())
.dataFetcher("products", productDataFetcher())
.dataFetcher("product", productByIdDataFetcher()))
.type("User", typeWiring -> typeWiring
.dataFetcher("orders", userOrdersDataFetcher())
.dataFetcher("profile", userProfileDataFetcher()))
.type("Order", typeWiring -> typeWiring
.dataFetcher("user", orderUserDataFetcher())
.dataFetcher("items", orderItemsDataFetcher()))
.type("Product", typeWiring -> typeWiring
.dataFetcher("category", productCategoryDataFetcher())
.dataFetcher("reviews", productReviewsDataFetcher()))
.type("Mutation", typeWiring -> typeWiring
.dataFetcher("createUser", createUserDataFetcher())
.dataFetcher("updateUser", updateUserDataFetcher())
.dataFetcher("deleteUser", deleteUserDataFetcher())
.dataFetcher("createOrder", createOrderDataFetcher())
.dataFetcher("updateOrder", updateOrderDataFetcher())
.dataFetcher("deleteOrder", deleteOrderDataFetcher()))
.build();
}
private String loadSchemaFromFile() {
// Load GraphQL schema from file
return """
type Query {
users(page: Int, size: Int, email: String, status: String): UserPage!
user(id: ID!): User
orders(page: Int, size: Int, userId: String, status: String): OrderPage!
order(id: ID!): Order
products(page: Int, size: Int, category: String, priceRange: PriceRange): ProductPage!
product(id: ID!): Product
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
deleteUser(id: ID!): Boolean!
createOrder(input: CreateOrderInput!): Order!
updateOrder(id: ID!, input: UpdateOrderInput!): Order!
deleteOrder(id: ID!): Boolean!
}
type Subscription {
userCreated: User!
userUpdated: User!
orderCreated: Order!
orderUpdated: Order!
}
type User {
id: ID!
email: String!
firstName: String!
lastName: String!
status: UserStatus!
createdAt: DateTime!
updatedAt: DateTime!
orders: [Order!]!
profile: UserProfile
}
type UserProfile {
id: ID!
bio: String
avatar: String
preferences: JSON
}
type Order {
id: ID!
userId: String!
user: User!
items: [OrderItem!]!
totalAmount: Decimal!
status: OrderStatus!
createdAt: DateTime!
updatedAt: DateTime!
}
type OrderItem {
id: ID!
productId: String!
product: Product!
quantity: Int!
price: Decimal!
}
type Product {
id: ID!
name: String!
description: String
price: Decimal!
stock: Int!
category: Category!
reviews: [Review!]!
status: ProductStatus!
createdAt: DateTime!
updatedAt: DateTime!
}
type Category {
id: ID!
name: String!
description: String
}
type Review {
id: ID!
userId: String!
user: User!
rating: Int!
comment: String
createdAt: DateTime!
}
type UserPage {
data: [User!]!
page: Int!
size: Int!
totalElements: Int!
totalPages: Int!
hasNext: Boolean!
hasPrevious: Boolean!
}
type OrderPage {
data: [Order!]!
page: Int!
size: Int!
totalElements: Int!
totalPages: Int!
hasNext: Boolean!
hasPrevious: Boolean!
}
type ProductPage {
data: [Product!]!
page: Int!
size: Int!
totalElements: Int!
totalPages: Int!
hasNext: Boolean!
hasPrevious: Boolean!
}
input CreateUserInput {
email: String!
firstName: String!
lastName: String!
password: String!
}
input UpdateUserInput {
firstName: String
lastName: String
status: UserStatus
}
input CreateOrderInput {
userId: String!
items: [CreateOrderItemInput!]!
}
input CreateOrderItemInput {
productId: String!
quantity: Int!
}
input UpdateOrderInput {
status: OrderStatus
items: [UpdateOrderItemInput!]
}
input UpdateOrderItemInput {
id: ID!
quantity: Int
}
input PriceRange {
min: Decimal
max: Decimal
}
enum UserStatus {
ACTIVE
INACTIVE
SUSPENDED
DELETED
}
enum OrderStatus {
PENDING
CONFIRMED
SHIPPED
DELIVERED
CANCELLED
}
enum ProductStatus {
ACTIVE
INACTIVE
OUT_OF_STOCK
DISCONTINUED
}
scalar DateTime
scalar Decimal
scalar JSON
""";
}
}
@TuskConfig
public class GraphQLAPIConfig {
private boolean enabled;
private String path;
private PlaygroundConfig playground;
private IntrospectionConfig introspection;
private SubscriptionsConfig subscriptions;
private RateLimitingConfig rateLimiting;
private CachingConfig caching;
// Getters and setters
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public String getPath() { return path; }
public void setPath(String path) { this.path = path; }
public PlaygroundConfig getPlayground() { return playground; }
public void setPlayground(PlaygroundConfig playground) { this.playground = playground; }
public IntrospectionConfig getIntrospection() { return introspection; }
public void setIntrospection(IntrospectionConfig introspection) { this.introspection = introspection; }
public SubscriptionsConfig getSubscriptions() { return subscriptions; }
public void setSubscriptions(SubscriptionsConfig subscriptions) { this.subscriptions = subscriptions; }
public RateLimitingConfig getRateLimiting() { return rateLimiting; }
public void setRateLimiting(RateLimitingConfig rateLimiting) { this.rateLimiting = rateLimiting; }
public CachingConfig getCaching() { return caching; }
public void setCaching(CachingConfig caching) { this.caching = caching; }
}
GraphQL Data Fetchers
import org.springframework.stereotype.Component;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import java.util.List;
import java.util.Map;@Component
public class UserDataFetcher {
private final UserService userService;
private final UserMapper userMapper;
public UserDataFetcher(UserService userService, UserMapper userMapper) {
this.userService = userService;
this.userMapper = userMapper;
}
public DataFetcher<List<User>> getUsers() {
return environment -> {
// Extract arguments
Integer page = environment.getArgument("page");
Integer size = environment.getArgument("size");
String email = environment.getArgument("email");
String status = environment.getArgument("status");
// Build filter criteria
UserFilterCriteria criteria = UserFilterCriteria.builder()
.email(email)
.status(status)
.build();
// Get users
Page<User> users = userService.getUsers(criteria,
PageRequest.of(page != null ? page : 0, size != null ? size : 20));
return users.getContent();
};
}
public DataFetcher<User> getUserById() {
return environment -> {
String id = environment.getArgument("id");
return userService.getUserById(id);
};
}
public DataFetcher<List<Order>> getUserOrders() {
return environment -> {
User user = environment.getSource();
return userService.getUserOrders(user.getId(), PageRequest.of(0, 100)).getContent();
};
}
public DataFetcher<UserProfile> getUserProfile() {
return environment -> {
User user = environment.getSource();
return userService.getUserProfile(user.getId());
};
}
public DataFetcher<User> createUser() {
return environment -> {
Map<String, Object> input = environment.getArgument("input");
CreateUserRequest request = CreateUserRequest.builder()
.email((String) input.get("email"))
.firstName((String) input.get("firstName"))
.lastName((String) input.get("lastName"))
.password((String) input.get("password"))
.build();
User user = userMapper.toEntity(request);
return userService.createUser(user);
};
}
public DataFetcher<User> updateUser() {
return environment -> {
String id = environment.getArgument("id");
Map<String, Object> input = environment.getArgument("input");
UpdateUserRequest request = UpdateUserRequest.builder()
.firstName((String) input.get("firstName"))
.lastName((String) input.get("lastName"))
.status(input.get("status") != null ?
UserStatus.valueOf((String) input.get("status")) : null)
.build();
User user = userMapper.toEntity(request);
user.setId(id);
return userService.updateUser(user);
};
}
public DataFetcher<Boolean> deleteUser() {
return environment -> {
String id = environment.getArgument("id");
userService.deleteUser(id);
return true;
};
}
}
@Component
public class OrderDataFetcher {
private final OrderService orderService;
private final OrderMapper orderMapper;
private final UserService userService;
public OrderDataFetcher(OrderService orderService,
OrderMapper orderMapper,
UserService userService) {
this.orderService = orderService;
this.orderMapper = orderMapper;
this.userService = userService;
}
public DataFetcher<List<Order>> getOrders() {
return environment -> {
Integer page = environment.getArgument("page");
Integer size = environment.getArgument("size");
String userId = environment.getArgument("userId");
String status = environment.getArgument("status");
OrderFilterCriteria criteria = OrderFilterCriteria.builder()
.userId(userId)
.status(status)
.build();
Page<Order> orders = orderService.getOrders(criteria,
PageRequest.of(page != null ? page : 0, size != null ? size : 20));
return orders.getContent();
};
}
public DataFetcher<Order> getOrderById() {
return environment -> {
String id = environment.getArgument("id");
return orderService.getOrderById(id);
};
}
public DataFetcher<User> getOrderUser() {
return environment -> {
Order order = environment.getSource();
return userService.getUserById(order.getUserId());
};
}
public DataFetcher<List<OrderItem>> getOrderItems() {
return environment -> {
Order order = environment.getSource();
return orderService.getOrderItems(order.getId());
};
}
public DataFetcher<Order> createOrder() {
return environment -> {
Map<String, Object> input = environment.getArgument("input");
CreateOrderRequest request = CreateOrderRequest.builder()
.userId((String) input.get("userId"))
.items(parseOrderItems((List<Map<String, Object>>) input.get("items")))
.build();
Order order = orderMapper.toEntity(request);
return orderService.createOrder(order);
};
}
private List<CreateOrderItemRequest> parseOrderItems(List<Map<String, Object>> items) {
return items.stream()
.map(item -> CreateOrderItemRequest.builder()
.productId((String) item.get("productId"))
.quantity((Integer) item.get("quantity"))
.build())
.collect(Collectors.toList());
}
}
🔧 Best Practices
1. RESTful API Design
- Use proper HTTP methods and status codes - Implement consistent error handling - Use pagination for large datasets - Implement proper caching strategies2. GraphQL Design
- Design schema for specific use cases - Use DataLoader for N+1 query prevention - Implement proper error handling - Use subscriptions for real-time updates3. API Versioning
- Use URL path versioning - Maintain backward compatibility - Provide migration guides - Deprecate old versions gracefully4. Security
- Implement proper authentication - Use role-based authorization - Validate all inputs - Rate limit API requests5. Documentation
- Use OpenAPI for REST documentation - Provide GraphQL playground - Include examples and schemas - Maintain up-to-date documentation🎯 Summary
TuskLang Java API design provides:
- RESTful APIs: Standard REST endpoints with pagination and filtering - GraphQL Support: Flexible query language with subscriptions - API Versioning: Proper versioning strategy with backward compatibility - Comprehensive Documentation: OpenAPI and GraphQL playground - Security: Authentication, authorization, and rate limiting
The combination of TuskLang's executable configuration with Java's API design capabilities creates a powerful platform for building scalable, maintainable, and well-documented APIs.
"We don't bow to any king" - Build APIs that developers love to use!