guidewire-sdk-patterns
Master Guidewire SDK patterns including Digital SDK, REST API Client, and Gosu best practices. Use when implementing integrations, building frontends with Jutro, or writing server-side Gosu code. Trigger with phrases like "guidewire sdk", "digital sdk", "jutro sdk", "guidewire patterns", "gosu best practices", "rest api client". allowed-tools: Read, Write, Edit, Bash(npm:*), Bash(gradle:*), Grep version: 1.0.0 license: MIT author: Jeremy Longshore <jeremy@intentsolutions.io>
Allowed Tools
No tools specified
Provided by Plugin
guidewire-pack
Claude Code skill pack for Guidewire InsuranceSuite (24 skills)
Installation
This skill is included in the guidewire-pack plugin:
/plugin install guidewire-pack@claude-code-plugins-plus
Click to copy
Instructions
# Guidewire SDK Patterns
## Overview
Master the key SDK patterns for Guidewire development: Digital SDK for frontends, REST API Client for integrations, and Gosu patterns for server-side development.
## Prerequisites
- Completed `guidewire-install-auth` and `guidewire-hello-world`
- Understanding of TypeScript/JavaScript (for Digital SDK)
- Familiarity with Gosu basics (for server-side patterns)
## Digital SDK (Frontend)
### Installation and Setup
```bash
# Initialize Jutro project with Digital SDK
npx @guidewire/jutro-cli@latest create my-portal --template agent-portal
cd my-portal
# Generate Digital SDK from your Cloud API
npx jutro-cli generate-sdk \
--api-url https://your-tenant.cloud.guidewire.com/pc \
--output src/sdk
```
### SDK Configuration
```typescript
// src/sdk/config.ts
import { createSdkConfig } from '@guidewire/digital-sdk';
export const sdkConfig = createSdkConfig({
baseUrl: process.env.REACT_APP_API_URL,
auth: {
type: 'oauth2',
clientId: process.env.REACT_APP_CLIENT_ID,
redirectUri: `${window.location.origin}/callback`,
scope: 'pc.policies'
},
headers: {
'Accept-Language': 'en-US'
},
timeout: 30000
});
```
### Entity Operations
```typescript
// Using Digital SDK for CRUD operations
import { useAccount, useAccounts, createAccount } from '../sdk/account';
// Read single entity
function AccountDetail({ accountId }: { accountId: string }) {
const { data: account, loading, error } = useAccount(accountId, {
include: ['accountHolder', 'primaryLocation']
});
if (loading) return ;
if (error) return ;
return (
);
}
// List entities with filtering
function AccountList() {
const { data: accounts, loading, pagination } = useAccounts({
filter: { accountStatus: 'Active' },
pageSize: 25,
sort: '-createdDate'
});
return (
);
}
// Create entity
async function handleCreateAccount(formData: AccountFormData) {
try {
const newAccount = await createAccount({
accountHolder: {
firstName: formData.firstName,
lastName: formData.lastName,
dateOfBirth: formData.dob
},
primaryLocation: {
addressLine1: formData.address,
city: formData.city,
state: { code: formData.state },
postalCode: formData.zip
}
});
console.log('Created account:', newAccount.accountNumber);
return newAccount;
} catch (error) {
handleApiError(error);
}
}
```
### Schema Validation
```typescript
// Using SDK-generated schema validation
import { accountSchema, validateAccount } from '../sdk/account';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
function AccountForm() {
const {
register,
handleSubmit,
formState: { errors }
} = useForm({
resolver: zodResolver(accountSchema)
});
return (
);
}
```
## REST API Client (Integration)
### Client Plugin Setup
```groovy
// build.gradle - REST API Client plugin
plugins {
id 'com.guidewire.rest-api-client' version '3.0.0'
}
restApiClient {
clients {
externalRating {
specFile = file('specs/rating-service.yaml')
packageName = 'gw.integration.rating'
generateSync = true
faultTolerance {
retryAttempts = 3
retryDelay = 1000
circuitBreakerThreshold = 5
}
}
}
}
```
### Generated Client Usage
```gosu
// Using generated REST API client
package gw.integration.rating
uses gw.integration.rating.api.RatingApi
uses gw.integration.rating.model.RatingRequest
uses gw.integration.rating.model.RatingResponse
uses gw.api.util.Logger
class RatingService {
private static final var LOG = Logger.forCategory("RatingService")
private static final var _api = new RatingApi()
static function getRating(policy : Policy) : RatingResponse {
var request = new RatingRequest()
request.PolicyNumber = policy.PolicyNumber
request.EffectiveDate = policy.EffectiveDate
request.CoverageType = policy.Product.Code
request.State = policy.PrimaryLocation.State.Code
try {
var response = _api.calculateRating(request)
LOG.info("Rating calculated: ${response.PremiumAmount}")
return response
} catch (e : FaultToleranceException) {
LOG.error("Rating service unavailable", e)
throw new RatingServiceException("Unable to calculate rating", e)
}
}
}
```
### Custom REST Client
```gosu
// Custom REST client implementation
package gw.integration.external
uses gw.api.rest.RestClient
uses gw.api.rest.RestClientBuilder
uses gw.api.rest.Response
uses gw.api.util.Logger
class ExternalServiceClient {
private static final var LOG = Logger.forCategory("ExternalServiceClient")
private var _client : RestClient
construct() {
_client = RestClientBuilder.create()
.withBaseUrl(ScriptParameters.ExternalServiceUrl)
.withTimeout(30000)
.withHeader("Authorization", "Bearer ${getToken()}")
.withHeader("Content-Type", "application/json")
.build()
}
function getResource(resourceId : String) : ExternalResource {
var response = _client.get("/resources/${resourceId}")
validateResponse(response)
return parseResponse(response, ExternalResource)
}
function createResource(resource : ExternalResource) : ExternalResource {
var response = _client.post("/resources", toJson(resource))
validateResponse(response)
return parseResponse(response, ExternalResource)
}
private function validateResponse(response : Response) {
if (response.StatusCode >= 400) {
LOG.error("API error: ${response.StatusCode} - ${response.Body}")
throw new ExternalServiceException(
"External service error: ${response.StatusCode}"
)
}
}
private function getToken() : String {
// Token retrieval logic
return ScriptParameters.ExternalServiceToken
}
}
```
## Gosu Patterns (Server-Side)
### Entity Query Patterns
```gosu
// Efficient querying with Query API
package gw.custom.query
uses gw.api.database.Query
uses gw.api.database.Relop
uses gw.api.path.Paths
class PolicyQueryService {
// Simple query
static function findByPolicyNumber(policyNumber : String) : Policy {
return Query.make(Policy)
.compare(Policy#PolicyNumber, Equals, policyNumber)
.select()
.AtMostOneRow
}
// Query with joins
static function findPoliciesByProducerCode(producerCode : String) : List {
return Query.make(Policy)
.join(Policy#ProducerCodeOfRecord)
.compare(ProducerCode#Code, Equals, producerCode)
.select()
.toList()
}
// Complex query with subqueries
static function findActiveHighValuePolicies(minPremium : java.math.BigDecimal) : List {
var query = Query.make(Policy)
query.compare(Policy#Status, Equals, PolicyStatus.TC_INFORCE)
query.compare(Policy#TotalPremiumRPT, GreaterThanOrEquals, minPremium)
query.compare(Policy#ExpirationDate, GreaterThan, Date.Today)
// Add subquery for specific coverage
var subquery = query.subselect(Policy#ID, CompareIn, PolicyPeriod#Policy)
subquery.join(PolicyPeriod#PersonalAutoLine)
.compare(PersonalAutoLine#PAVehicles, IsNotNull, null)
return query.select()
.orderBy(\p -> p.TotalPremiumRPT)
.toList()
}
// Batch processing pattern
static function processInBatches(
query : Query,
batchSize : int,
processor(batch : List)
) {
var iterator = query.select().iterator()
var batch = new ArrayList()
while (iterator.hasNext()) {
batch.add(iterator.next())
if (batch.size() >= batchSize) {
processor(batch)
batch.clear()
}
}
if (!batch.Empty) {
processor(batch)
}
}
}
```
### Transaction Patterns
```gosu
// Transaction management patterns
package gw.custom.transaction
uses gw.api.database.Transaction
uses gw.transaction.Transaction as GWTransaction
class TransactionService {
// Basic transaction
static function createAccountWithPolicy(accountData : AccountData) : Policy {
return GWTransaction.runWithNewBundle(\bundle -> {
var account = new Account(bundle)
account.AccountNumber = accountData.AccountNumber
var contact = new Person(bundle)
contact.FirstName = accountData.FirstName
contact.LastName = accountData.LastName
account.AccountHolderContact = contact
// Create submission
var submission = new Submission(bundle)
submission.Account = account
submission.Product = accountData.Product
return submission.Policy
})
}
// Transaction with savepoint
static function complexOperation(policy : Policy) {
GWTransaction.runWithNewBundle(\bundle -> {
policy = bundle.add(policy)
try {
// Risky operation 1
updateCoverages(policy)
// Risky operation 2
applyEndorsement(policy)
} catch (e : Exception) {
// Bundle will rollback automatically on exception
throw e
}
})
}
// Async transaction
static function queueAsyncWork(policyId : Key) {
gw.api.async.AsyncProcess.schedule(
"ProcessPolicy",
\-> processPolicy(policyId),
Date.Now
)
}
}
```
### Plugin Patterns
```gosu
// Plugin implementation pattern
package gw.plugin.rating
uses gw.plugin.rating.IRatingPlugin
uses gw.api.util.Logger
class CustomRatingPlugin implements IRatingPlugin {
private static final var LOG = Logger.forCategory("CustomRatingPlugin")
override function calculatePremium(period : PolicyPeriod) : Money {
LOG.info("Calculating premium for policy: ${period.PolicyNumber}")
var basePremium = calculateBasePremium(period)
var discounts = applyDiscounts(period, basePremium)
var taxes = calculateTaxes(period, basePremium - discounts)
return basePremium - discounts + taxes
}
private function calculateBasePremium(period : PolicyPeriod) : Money {
// Base premium calculation logic
return period.Lines
.map(\line -> line.calculatePremium())
.sum()
}
private function applyDiscounts(period : PolicyPeriod, base : Money) : Money {
var totalDiscount = Money.ZERO
// Multi-policy discount
if (period.Account.Policies.Count > 1) {
totalDiscount += base * 0.10
}
// Good driver discount
if (hasGoodDriverDiscount(period)) {
totalDiscount += base * 0.15
}
return totalDiscount
}
}
```
## Output
- Configured Digital SDK with type-safe API calls
- REST API Client with fault tolerance
- Gosu patterns following Guidewire best practices
## Error Handling
| Error | Cause | Solution |
|-------|-------|----------|
| SDK generation failed | Invalid API spec | Verify OpenAPI spec URL and access |
| Type mismatch | Schema changed | Regenerate SDK |
| Transaction timeout | Long-running operation | Optimize or use async |
| Client timeout | Network issues | Increase timeout, add retry |
## Resources
- [Digital SDK Documentation](https://docs.guidewire.com/jutro/documentation/10.12/working-with-cloud-apis/)
- [REST API Client Guide](https://developer.guidewire.com/rest-api-client/)
- [Gosu Language Reference](https://gosu-lang.github.io/)
- [Query API Documentation](https://docs.guidewire.com/cloud/pc/202503/cloudapica/)
## Next Steps
For core insurance workflows, see `guidewire-core-workflow-a` and `guidewire-core-workflow-b`.
{account.accountHolder.displayName}
Account #: {account.accountNumber}
Status: {account.accountStatus.name}